Working with Django 1.11 and a postgreSQL Database (just switched from sqlite and didn't have this problem before)
So I have 3 models:
models.py
class Person(models.Model):
is_parent = models.BooleanField()
class VideoGamePurchase(models.Model):
bought_by = models.ForeignKey(Person)
after_homework = models.OneToOneField(HomeWork, OPTIONS???)
class HomeWork(models.Model):
done_by = models.ForeignKey(Person)
content = models.CharField(blablaba)
So the logic I'm try to implement is that if Person.is_parent is True a VideoGamePurchase instance can be created with an empty or null field for after_homework. However, if Person.is_parent is False, I want this field to be the primary key of a unique HomeWork object.
I can't find the right options to achieve this:
If I don't have primary_key=True then the makemigrations fails:
You are trying to add a non-nullable field 'id' to video_game_purchase 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 with a null value for this column)
2) Quit, and let me add a default in models.py
so I guess I hav to have primary_key=True. But then it seems like I can't have null=Trueor blank=True.
Is there a way to have a OneToOneField optionally empty with postgreSQL?
Is there a other/simpler way to implement this sort of logic?
Thanks for your help!
If you want the after_homework field to be optional, then you should use null=True and blank=True.
class VideoGamePurchase(models.Model):
bought_by = models.ForeignKey(Person)
after_homework = models.OneToOneField(HomeWork, null=True, blank=True)
You don't want primary_key=True for the after_homework - that would make the after_homework the primary key field of the VideoGamePurchase model, which doesn't make sense if the field is optional.
It looks like your migrations are messed up because you had primary_key=True for the after_homework field earlier. The easiest fix would be to start with a fresh database, delete the migrations for that app, then rerun makemigrations and migrate. This time, the migration will automatically create a primary key field id for the VideoGamePurchase model.
Related
I added the field user which is a foreign key to another model called User. This field was added to the model called Bid. However, when I tried to migrate the changes, I got the message:
It is impossible to add a non-nullable field 'user' to bid without specifying a default. This is because 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 with a null value for this column)
2) Quit and manually define a default value in models.py.
Last time, I set it to 'user' and got an error that stated: ValueError: invalid literal for int() with base 10: 'user'.
What should I set the default value as?
models.py:
class Bid(models.Model):
item = models.ForeignKey(Listing, on_delete=models.CASCADE)
price = models.FloatField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
As the error says, you are creating a new field in your table. When creating a new field, existing rows need to be taken into consideration. Safest approach is to set it as null=True and handle the field later.
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
However you may not want a Bid to have a null user. In which case I recommend looking into how to write a custom migration to populate existing rows.
Another note: if the data you have in your table is not needed, you could consider dropping the table and rebuilding it or deleting your migrations and creating them again with manage.py makemigrations - again only if the data you have in your db is not needed.
If you add a relationship via a new foreign key, you have two options
You make the new FK nullable and let it default to NULL (i.e. None in python). This way, legacy entries with unknown relations will have NULL as the FK, i.e. do not know their users.
You manually populate the legacy fields with the appropriate foreign keys to the Bid records. This requires that you have kept that information beforehand.
In my models.py, I have the request_author field defined as:
request_author = models.ForeignKey(
User,
on_delete = models.CASCADE
)
This makes the request_author field to be registered with a user code (int) number in the API database. However, I would like to have it as the user name. How do I do that?
You can use the to_field=... parameter [Django-doc], for that:
The field on the related object that the relation is to. By default, Django uses the primary key of the related object. If you reference a different field, that field must have unique=True.
So you can here set the to_field to 'username':
request_author = models.ForeignKey(
User,
on_delete = models.CASCADE,
to_field='username'
)
The username of the User model is unique=True, so this will not be a problem here. If you define a custom user model, than this does not per se holds.
Note that if you change the to_field, this might fail, since now it refers to a different field. In case you are just starting to build an application, it might be better to drop the database, remove old migrations, and then create a new migration file.
From collegues I inherited multiple identical MySQL databases. Using DJANGO's inspectdb I derived the data models for it, and created a web interface to view the data. When instantiating the model structure again, DJANGO failed to create a unique_together contraint for 2 fields.
Problem:
I want to get rid of the existing unique_together and the 2 primary keys, as DJANGO does not support mutiple primary keys. For example with the DJANGO auto generated id field (as primary key). Is this possible, and how should I do it? Writing a custom migration would be an option, but how?
Contraints
Data loss is not an option, so I cannot just drop tables.
Also the migration history should be maintained.
What I have is:
class MyModel(models.Model):
sessionid = models.ForeignKey('Session', models.DO_NOTHING, db_column='sessionID', primary_key=True)
datetime = models.BigIntegerField(primary_key=True)
class Meta:
unique_together = (('sessionid', 'datetime'),)
But it should become something like:
class MyModel(models.Model):
sessionid = models.ForeignKey('Session', models.DO_NOTHING, db_column='sessionID')
datetime = models.BigIntegerField()
Any help is highly appreciated!
EDIT 1
When removing the prmimary keys, DJANGO attempts to create the desired id autofield. However, as data exists in the table, DJANGO requires default initial values for this. Though a default value is not allowed, as the primary key (autofields are primary keys) must be unique.
It's 3 years late, but I hope that it will help someone. When you remove primary_key attr in all your model fields, Django will try to create an AutoField field named id, which will be the primary_key of your model, but when you already have data in the DB, you must set a default value for existing entries which will cause another issue because primary_key is unique.
But a solution can be to create manually an primary_key field named id in your SQL table.
Remove the current primary key
ALTER TABLE tablename DROP PRIMARY KEY;
Create a new primary key field
ALTER TABLE tablename ADD id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
If you want that Django takes this field as default primary key, you must set its name as id.
Make migrations on you django app.
Delete all your migrations files in the corresponding app and re-run python manage.py makemigrations. Your code must be like:
class MyModel(models.Model):
sessionid = models.ForeignKey('Session', models.DO_NOTHING, db_column='sessionID')
datetime = models.BigIntegerField()
...
We are trying to work with legacy DB Tables that were generated outside of Django and are not structured in an ideal way. We also can not modify the existing tables.
The DB uses the same user ID (pk) across all the tables, wether or not there is a record for that user ID. It also uses that ID as a PK on the other tables, rather than rely on them to auto increment their own IDs.
So imagine something like this below:
class Items(models.Model):
user_id = models.ForeignKey('User', db_column='UserID')
class User(models.Model):
user_id = models.IntegerField(primary_key=True)
class UserTypeA(models.Model):
user_id = models.IntegerField(primary_key=True) # Same Value as User
class UserTypeB(models.Model):
user_id = models.IntegerField(primary_key=True) # Same Value as User
What we thought of creating a relationship between Items and UserTypeA (as well as UserTypeB) is to create another field entry that uses the same column as the user_id.
class Items(models.Model):
user_id = models.ForeignKey('User', db_column='UserID')
user_type_a = models.ForeignKey('UserTypeA', db_column='UserID')
user_type_b = models.ForeignKey('UserTypeB', db_column='UserID')
This unfortunately returns a "db_column is already used" type error.
Any thoughts on how to better approach the way what we're trying to do?
A detail to note is that we're only ever reading from this databases (no updates to), so a read-only solution is fine.
Thanks,
-RB
I've solved a similar problem with this (this code should be put before the definition of your Model):
from django.db.models.signals import class_prepared
def remove_field(sender, **kwargs):
if sender.__name__ == "MyModel":
sender._meta.local_fields.remove(sender.myFKField.field)
class_prepared.connect(remove_field)
(Tested in Django 1.5.11)
Django uses local_fields to make the CREATE TABLE query.
So, I've just attached the signal class_prepared and check if sender equals the class I was expecting. If so, I've removed the field from that list.
After doing that, the CREATE TABLE query didn't include the field with same db_column and the error did not ocurr.
However the Model still working properly (with manager methods properly populating the removed field from local_fields), I can't tell the real impact of that.
I'm new to django and trying to have a Foreign key back to users for an assignee and reporter.
But when i'm trying to apply the change with South i get the error
ValueError: The database backend does not accept 0 as a value for AutoField.
My Model Code:
class Ticket(models.Model):
title = models.CharField(max_length=80)
text = models.TextField(blank=True)
prioritys = models.ForeignKey(Prioritys)
ticket_created = models.DateTimeField(auto_now_add=True)
ticket_updated = models.DateTimeField(auto_now=True)
assignee = models.ForeignKey(User, null=True, related_name='assignee')
reporter = models.ForeignKey(User, null=True, related_name='reporter')
def escaped_text(self):
return markdown.markdown(self.text)
def __unicode__(self):
return self.text
If this happens when you run manage.py migrate (or manage.py syncdb in old versions), the reason maybe is that you have tried to add a foreign key to a model which uses AutoField as its primary key, and use 0 as the default value. Edit the migration file and remove the argument default=0 in AddField operations. It works for me in Django 1.10.
I wasn't using South, but I recently upgraded from Django 1.4 to 1.6 (with MySQL as db backend for both), and was getting the same ValueError when trying to save some models. I tracked it down to a field that was a recursive ForeignKey. So I had:
class Foo(models.Model):
...
duplicate = models.ForeignKey('self', blank=True, null=True)
...
Somewhere along the line—unfortunately I'm not sure where—many of my objects had gotten the value of 0 for duplicate_id.
>>> Foo.objects.filter(duplicate_id=0).count()
2078
This didn't occur for any of my other ForeignKey fields, only the self-referential one. So I set the values of that field back to None, and this fixed the error.
>>> Foo.objects.filter(duplicate_id=0).update(duplicate=None)
2078L
Because this particular error doesn't point you to a specific field that's causing problems, in general you can check if a ForeignKey field fieldname has any 0 values:
>>> Foo.objects.filter(fieldname_id=0).count()
If this gives a non-zero result, that field probably needs to be fixed.
A long-long time ago there was a problem with Autofield.
https://code.djangoproject.com/ticket/17653
an interesting quotes:
A:
It seems you are trying to save 0 to a ForeignKey which points to an
AutoField. But, this is illegal, as the AutoField will not accept that
value and so the ForeignKey can not hold that value either.
B:
So this fix creates another problem when you need to be able to accept
a value of 0 (or if you are working on a DB that already has a value
of 0!) in an autofield. In my case, I need to accept an ID of zero so
that my foreign key can point to zero so that a unique constraint can
work properly.
It looks like you have 0 in "user"."user_id".
But again... Full StackTrace, please...
I got the same error after upgrading to django 1.7.1
it was caused when saving a model that had a foreign key to another model that for some reason had an id starting auto increment with 0, i guess this was allowed in previous django versions, but not now.