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()
...
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.
These are my requirements, and I can not change them:
I have a field named name. That is my primary key
I have a field named id, but it is not my primary key.
The id is also an auto-increment field.
I have several problems:
Auto-increment fields must be primary keys in django
Django sets primary_key in fields called id:
I have been able to work around the first problem, by using this:
from django.db.models.fields import AutoField
from django.db.models.fields import checks
class AutoFieldNonPrimary(AutoField):
def _check_primary_key(self):
if self.primary_key:
return [
checks.Error(
"AutoFieldNonPrimary must not set primary_key=True.",
obj=self,
id="fields.E100",
)
]
else:
return []
Now I can do:
class TheNiceUser(models.Model):
id = AutoFieldNonPrimary()
name = models.CharField(max_length=16, primary_key=True)
But still, the id is declared as primary key. Now I have two primary keys in my model, and the migrations complain.
According to the django doc, this should not be the case: if a field has primary_key set, django should not add an id field as primary key.
But what happens if I declare an id field? It seems that django insists on making that a primary key, even if another field is already declared as primary key.
How can I get out of this situation?
I've got a django model that has a self, asymmetrical many to many relationship.
I would like that relationship to reference objects using a unique identifying field other than the table's primary key field.
I could not find any documentation about specifying a field to the ManyToMany object's foreign key used to identify the related objects. Is there a way to do that?
Here's an example:
class Annotation(models.Model):
uuid = models.UUIDField(null=True, unique=True, editable=False)
data = models.TextField()
dependencies = models.ManyToManyField('self', symmetrical=False,
related_name="dependents")
In the example above, dependencies will be a mapping of IntegerFields to IntegerFields because that's the field type of the id primary key implicitly created in Annotation.
Instead, I wish the relationship table (used to maintain the relationship) will use the uuid field I manually defined (instead of the id field).
Ideally I would assume replacing self (first parameter to ManyToManyField) with self.uuid or Annotation.uuid will do just that, but unfortunately this errors with the following:
ValueError: Related model 'Annotation.uuid' cannot be resolved
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.
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.