Forked django-oscar app with custom model unable to migrate - python

I am using django-oscar and forked some of the apps to use a custom model.
Specifically with the catalogue model. By default there are products, product types and product categories. I am trying to extent the model to have a collections table, and to have each product be associated with a collection.
I want to make the relationship so that when a collection is deleted all associated products are deleted, but so that deleting a product does not delete a collection.
Aside from adding a new collection table, I extent the product table to have a multiplier field (which will contain an integer used to multiply the wholesale price...if there is a better way to do this please inform) and a foreign key to the collections table.
Based on my understanding everything looks good. This is my models.py from the forked catalogue app:
from django.db import models
class Collection(models.Model):
name = models.CharField(max_length=50)
prod_category = models.CharField(max_length=50)
description = models.TextField()
manufacturer = models.TextField()
num_products = models.IntegerField()
image_url = models.URLField()
from oscar.apps.catalogue.abstract_models import AbstractProduct
class Product(AbstractProduct):
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
multiplier = models.DecimalField(max_digits=2, decimal_places=1)
from oscar.apps.catalogue.models import *
When I do makemigrations via manage.py, after asking for default values (something I will deal with later) it seems fine.
When running migrate however, I get the following error output:
Running migrations:
Applying catalogue.0014_auto_20181211_1617...Traceback (most recent call last):
File "/home/mysite/lib/python3.7/Django-2.1.3-py3.7.egg/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
psycopg2.IntegrityError: insert or update on table "catalogue_product" violates foreign key constraint "catalogue_product_collection_id_e8789e0b_fk_catalogue"
DETAIL: Key (collection_id)=(1) is not present in table "catalogue_collection".
Is this because I need to add a field collection_id?

The problem is that you have specified a default value that doesn't exist. I am guessing that when asked to specify default values for the migration, you entered 1 for the collection. The problem is that there is no Collection object with such an ID in the database, which is why the migration fails.
You either need to:
Create a Collection object with the default ID you specified before attempting to add the overridden product model.
Make the foreign key to Collection nullable, so that you don't need a default value.

Related

Get all the rows of a table along with matching rows of another table in django ORM using select_related

I have 2 models
Model 1
class Model1(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255)
type = models.CharField(max_length=255)
details = models.TextField(max_length=1000)
price = models.FloatField()
Model 2
class Model2(models.Model):
id = models.IntegerField(primary_key=True)
user_id = models.ForeignKey(
User,
on_delete=models.CASCADE
)
plan_selected = models.ForeignKey(Model1)
I am trying to check whether a user has selected any plans.
The field plan_selected is a foreign key for Model1 - id. I want to get all details of Model1 along with details of Model2 in a single line of the query set using Django.
So far I have tried to get is :
sub_details = Model1.objects.select_related('Model2').filter(user_id=id)
For select_related(), you want to select on the field name, not the related model's name. But all this does is that it adds a join, pulls all rows resulting from that join, and then your python representations have this relation cached (no more queries when accessed).
You also need to use __ to traverse relationships across lookups.
Docs here: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related
But you don't even need select_related() for your goal: "I am trying to check whether a user has selected any plans.". You don't need Model1 here. That would be, given a user's id user_id:
Model2.objects.filter(user_id=user_id).exists()
# OR if you have a User instance `user`:
user.model2_set.exists()
If however what you want is "all instances of Model1 related to user via a Model2":
Model1.objects.filter(model2__user_id=user_id).all()
to which you can chain prefetch_related('model2_set') (this is 1 -> Many so you're pre-fetching, not selecting - i.e fetches and caches ahead of time each results' related model2 instances in one go.)
However, that'd be easier to manage with a ManyToMany field with User on Model1, bypassing the need for Model2 entirely (which is essentially just a through table here): https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

How can I add an auto increment field in a model based on total count of objects with same field value?

I'm new to the whole Django thing and a bit lost. Sorry if the title is a bit confusing I'll try to clear things out.
So basically I have two models (Folder and Document). A Document can have a single Folder as one of its fields using a Foreign Key. Now I have another field in Document that needs to get the value of the total Document objects that share the same Folder and increase by one.
I've tried things I read on the docs (aggregation, F() objects, overriding model's save() function) as well as some answers is read here but didn't manage to get it right so I'm posting to get some help. Below is my models.py file with the two models and some comments for better understanding.
class Folder(models.Model):
category = models.IntegerField()
subCategory = models.IntegerField()
name = models.CharField(max_length= 50)
desc = models.TextField()
class Document(models.Model):
folder = models.ForeignKey(Folder, on_delete=models.CASCADE)
date_created = models.DateField()
date_added = models.DateTimeField()
#The field below needs to sum all Document objects that share
#the same folder value in the database + 1 and set it as its default value
f_no = models.IntegerField(default=lambda: Document.objects.aggegate(Count('folder')) + 1)
Thank you in advance, any leads or clues are most welcome
EDIT:
Forgot to say that all management is done via Django's admin dashboard
if this has anything to do at all with my situation. I registered both
models in admin.py and that's all. I make new Folder objects when needed
and save Documents with one specific Folder in them each time
I would recommend creating a ManyToMany relation in the Folder, and add a created Document object into the Folder's ManyToMany relation.
Models.py
class Folder(models.Model):
category = models.IntegerField()
subCategory = models.IntegerField()
name = models.CharField(max_length= 50)
desc = models.TextField()
documents = models.ManyToManyField('app.Document')
You can add can add documents to the folder by using .add() to the ManyToMany relation and the amount of documents in the relation by using .count()
ManyToMany relations are well documented here:
https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/
When you create a Document model object, it represents a single item. That single item shouldn't have a count of how many documents are in a file. The f_no you note should actually be in the Folder model.
Once you create a Document object that is related to a Folder object via ForeignKey, you can use signals to increment the f_no field that resides in the Folder object.
#receiver(post_save, sender=Document)
def increment_folder_item_count(sender, **kwargs):
# get Folder object via Document model instance folder foreignkey field
# folder.f_no += 1
# folder.save()

django error on migration: "There is no unique constraint matching given keys for referenced table

So I have seen that a lot of these kinds of questions have popped up (few answered) and none in a Django aspect that I saw. I am confused why I am getting the error, I am guessing i am missing something on my field decorator or what not in my model definition. Here are the two models... (one abbreviated). I thought I did everything right with unique and primary key set to true in the one table that the foreign key gives reference to but upon migrate I get this error:
django.db.utils.ProgrammingError: there is no unique constraint matching given keys for referenced table "swsite_zoneentity"
Edit up dated the code ...
class ZoneEntity(models.Model):
zone_number = models.CharField(max_length=100, primary_key=True)
mpoly = models.PolygonField() #this should grow and shrink for the most representative one...
objects = models.GeoManager()
created_at=models.DateField(auto_now_add=True)
updated_at=models.DateField(auto_now=True)
class CesiumEntity(models.Model):
be_number = models.CharField(max_length=100) #the number assigned to a foot print to distinguish
#zone_id = models.CharField(max_length=100, null=True, blank=True)
zone_id = models.ForeignKey('ZoneEntity', null=True, blank=True)
To solve this, needed to add the unique constraint on the postgres table id myself.
psql <your-database-name>
ALTER TABLE swsite_zoneentity ADD CONSTRAINT zone_unique_id UNIQUE(id);
Like this answer
This problem appears most times because you copied or created your database from a dump and somewhere the unique constraint on your primary key column(as well as other constraints got lost.
Solution:
Open your DB with pg4admin or any client, Databases>your_database>schema>public>tables>your_table right-click
on the table name,
Choose Properties
Select columns tabs
switch primary key on your pk column
save/exit
run migration again
Codejoy,
When you define a primarykey, it is automatically set as unique.. So, just go by:
class ZoneEntity(models.Model):
zone_number = models.CharField(max_length=100, primary_key=True)
....
class CesiumEntity(models.Model):
...
zone_id = models.ForeignKey('ZoneEntity', null=True, blank=True)
...
This will automatically bind the PK of ZoneEntity with zone_id!
If the field you are trying to make the relation IS NOT the primary key, then you can add unique=True and to_field='foo'
- python manage.py. makemigration
s
Migrations for 'module1':
0002_auto_20170214_1503.py:
- Create model CesiumEntity
- Create model ZoneEntity
- Add field zone_id to cesiumentity
- python manage.py migrate
Operations to perform:
Synchronize unmigrated apps: staticfiles, messages
Apply all migrations: admin, contenttypes, module1, auth, sessions
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying module1.0002_auto_20170214_1503... OK
I too had same issue while migrating DB from SQLite to PostgreSQL 14.4, even when referenced Foreign key had primary_key=True set.
Deleting the old migrations, solved my issue.

how to update model in django by adding new primary key field?

How to replace default primary key in Django model with custom primary key field?
I have a model with no primary key defined at first since django automatically adds an id field by default as primary field.
#models.py
from django.db import models
class Event(models.Model):
title = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=150)
I added some objects into it from django shell.
>>e = Event('meeting', 'Contents about meeting')
>>e.save()
>>e = Event('party', 'Contents about party')
>>e.save()
Then I require to add custom character field as primary into this model.
class Event(models.Model):
event-id = models.CharField(max_length=50, primary_key=True)
...
Running makemigrations:
$ python manage.py makemigrations
You are trying to add a non-nullable field 'event-id' to event without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and `django.utils.timezone modules` are available, so you can do e.g. timezone.now()
>>> 'meetings'
Migrations for 'blog':
0002_auto_20141201_0301.py:
- Remove field id from event
- Add field event-id to event
But while running migrate it threw an error:
.virtualenvs/env/local/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py", line 485, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: blog_event__new.event-id
In my experience (using Django 1.8.* here), I've seen similar situations when trying to update the PK field for models that already exist, have a Foreign Key relationship to another model, and have associated data in the back-end table.
You didn't specify if this model is being used in a FK relation, but it seems this is the case.
In this case, the error message you're getting is because the data that already exists needs to be made consistent with the changes you're requesting --i.e. a new field will be the PK. This implies that the current PK must be dropped for django to 'replace' them. (Django only supports a single PK field per model, as per docs[1].)
Providing a default value that matches currently existing data in the related table should work.
For example:
class Organization(models.Model):
# assume former PK field no longer here; name is the new PK
name = models.CharField(primary_key=True)
class Product(models.Model):
name = models.CharField(primary_key=True)
organization = models.ForeignKey(Organization)
If you're updating the Organization model and products already exist, then existing product rows must be updated to refer to a valid Organization PK value. During the migration, you'd want to choose one of the existing Organization PKs (e.g. 'R&D') to update the existing products.
[1] https://docs.djangoproject.com/en/1.8/topics/db/models/#automatic-primary-key-fields
Django has already established an auto incrementing integer id as primary key in your backend as and when u made the previous model.
When u were trying to run the new model , An attempt was made to recreate a new primary key column that failed.
Another reason is,When u made the field,Django was expecting a unique value be explicitly defined for each new row which it couldn't found,hence the reason.
As told in previous answer you can re-create the migration and then try doing it again.It should work.. cheers :-)
The problem is that you made the field unique, then attempted to use the same value for all the rows in the table. I'm not sure if there's a way to programmatically provide the key, but you could do the following:
Delete the migration
Remove the primary_key attribute from the field
Make a new migration
Apply it
Fill in the value for all your rows
Add the primary_key attribute to the field
Make a new migration
Apply it
It's bruteforce-ish, but should work well enough.
Best of luck!

Django/South: save() not working as expected when adding a foreign key to a new table

I've just started to study the South framework, waiting for the 1.7 release of Django to be released and production-ready.
I'm in this starting situation:
class TableA(models.Model):
CustomEntityA_ctype = models.ForeignKey(ContentType, related_name="tableB_related_name")
CustomEntityA_oid = models.PositiveIntegerField()
CustomEntityA = generic.GenericForeignKey('CustomEntityA_ctype',
'CustomEntityA_oid')
I'd like these scheme to be migrated into something like this
class TableB(models.Model):
CustomEntityB_ctype = models.ForeignKey(ContentType, related_name="tableB_related_name")
CustomEntityB_oid = models.PositiveIntegerField()
CustomEntityB = generic.GenericForeignKey('CustomEntityB_ctype',
'CustomEntityB_oid')
class TableA(models.Model):
tableB_entity = models.ForeignKey(TableB,
related_name='tableA_related_name',
null=False)
In order to get this, I've setup a proper initial migration, then my intermediate scheme is something like this
class TableB(models.Model):
CustomEntityB_ctype = models.ForeignKey(ContentType, related_name="tableB_related_name")
CustomEntityB_oid = models.PositiveIntegerField()
CustomEntityB = generic.GenericForeignKey('CustomEntityB_ctype',
'CustomEntityB_oid')
class TableA(models.Model):
CustomEntityA_ctype = models.ForeignKey(ContentType, related_name="tableB_related_name")
CustomEntityA_oid = models.PositiveIntegerField()
CustomEntityA = generic.GenericForeignKey('CustomEntityA_ctype',
'CustomEntityA_oid')
tableB_entity = models.ForeignKey(TableB,
related_name='tableA_related_name',
null=True)
As for South tutorial, I'm trying to split migration in three parts:
A first --auto migration towards this intermediate model
A datamigration generating the migration python script
A final --auto migration towards the final model
This is the content of my forwards function
def forwards(self, orm):
"Write your forwards methods here."
# Note: Don't use "from appname.models import ModelName".
# Use orm.ModelName to refer to models in this application,
# and orm['appname.ModelName'] for models in other applications.
for tableA_entity in orm.TableA.objects.all():
ctype = tableA_entity.CustomEntityA_ctype
oid = tableA_entity.CustomEntityA_oid
tableB_entity = orm.TableB.objects.create(CustomEntityB_ctype=ctype,
CustomEntityB_oid=oid,
)
print "created a tableB_entity", tableB_entity
tableA_entity.tableB_entity = tableB_entity
tableA_entity.save()
tableA_entity_FROMDB = orm.TableA.objects.all()[0]
print "tableA_entity_FROMDB.tableB_entity: ", tableA_entity_FROMDB.tableB_entity
When I invoke the migration step, I get a correctly created and printed tableB_entity, but when I print the result of the query with the last two rows, I get an empty result. The overall result is that the save() function seems not to be working at all. If I enter the manage.py shell and query the models I get the expected result in TableB but an empty foreign key for the corresponding entity in TableA.
Is there anyone who might explain this to me?
Thanks a lot in advance!
The only way I found to let it work so far was to split the scheme and data migration in two different sets of migrations.
In the first one I schema/data migrated towards TableB having those three more fields.
In the second one, I added the foreign key to TableA and assigned the TableB entity created at the previous step to the corresponding TableA entity.
Obviously, this is not optimal: if in the first step an entity target of a many-to-one relation should be created, during the second migration it would not be straightforward which entity of TableA should point to the newly created item. A way to mitigate this might be creating a temporary reverse foreign key from TableB to TableA useful to find the original TableA entity which to refer to and then remove it in a further migration.
Before accepting this, I'll wait some more time in order to let someone else come up with the "correct" answer :)

Categories