In Django, I am trying to move a custom User model from one app to another. When I follow instructions such as found here, and try to apply the migration, I get errors with ValueError: Related model 'newapp.user' cannot be resolved that originate from django.contrib.admin.models.LogEntry.user, which is defined as models.ForeignKey(settings.AUTH_USER_MODEL, ...), so that is a model from the Django admin (which I also use) that has a foreign key to the User model. How can I do this migration?
For swappable models (models that can be swapped out through a value in settings) such a move is non-trivial. The root cause of the problem here is that LogEntry has a foreign key to settings.AUTH_USER_MODEL, but the history of settings.AUTH_USER_MODEL itself is not managed or known to Django migrations. When you change AUTH_USER_MODEL to point to the new User model, that retroactively changes the migration history as Django sees it. To Django it now looks like LogEntry's foreign key has always referenced the new User model in destapp. When you run the migration that creates the table for LogEntry (e.g. when re-initializing the database or running tests), Django cannot resolve the model and fails.
See also this issue and the comments there.
To work around this problem, AUTH_USER_MODEL needs to point to a model that exists in the initial migration for the new app. There are a few approaches to get this working. I'll assume the User model is moved from sourceapp to destapp.
Move the migration definition for the new User model from the last migration (where Django makemigrations would automatically put it) to the initial migration of destapp. Leave it wrapped in a SeparateDatabaseAndState state operation, because the database table is already created by the migrations in the sourceapp. Then, you'll need to add a dependency from the initial migration of destapp to the last migration of sourceapp. The problem is that if you try to apply the migrations as they are now, it will fail because destapp's initial migration has already been applied while it's dependency (sourceapp's last migration) has not been. So you will need to apply the migrations in sourceapp before adding the above migrations in destapp. In the gap between applying sourceapp and destapp's migrations the User model won't exist so your application will be temporarily broken.
Apart from temporarily breaking the application, this has the other problem that now destapp will depend on sourceapp's migrations. If you can do that, that's fine, but if there already exists a dependency from a sourceapp migration to a destapp migration this won't work and you've now created a circular dependency. If that's the case, look at the next options.
Forget about the User migration history. Just define the User class in destapp's initial migration, without the SeparateDatabaseAndState wrapper. Make sure you have CreateModel(..., options={'db_table': 'sourceapp_user'}, ...), so the database table will be created the same as it would when User lived in sourceapp. Then edit sourceapp's migration(s) where User is defined, and remove those definitions. After that, you can create a regular migration where you remove User's db_table setting so the database table gets renamed to what it should be for destapp.
This only works if there are no or minimal migrations in the migration history of sourceapp.User. Django now thinks User always lived in destapp, but its table was named as sourceapp_user. Django cannot track any database-level changes to sourceapp_user anymore since that information was removed.
If this works for you, you can either forego any dependencies between sourceapp and destapp, if sourceapp's migrations don't need User to be there, or have sourceapp's initial migration depend on destapp's initial migration so that the table for User is created before sourceapp's migrations are run.
If both don't work in your situation, another option is to add the definition for User to sourceapp's initial migration (without SeparateDatabaseAndState wrapper), but have it use a dummy table name (options={'db_table': 'destapp_dummy_user'}). Then, in the newest migration where you actually want to move User from sourceapp to destapp, do
migrations.SeparateDatabaseAndState(database_operations=[
migrations.DeleteModel(
name='User',
),
], state_operations=[
migrations.AlterModelTable('User', 'destapp_user'),
])
This will delete the dummy table in the database, and point the User model to the new table. The new migration in sourceapp should then contain
migrations.SeparateDatabaseAndState(state_operations=[
migrations.DeleteModel(
name='User',
),
], database_operations=[
migrations.AlterModelTable('User', 'destapp_user'),
])
so it is effectively the mirror image of the operation in the last destapp migration. Now only the last migration in destapp needs to depend on the last migration in sourceapp.
This approach appears to work, but it has one big disadvantage. The deletion of destapp.User's dummy database table also deletes all foreign key constraints to that table (at least on Postgres). So LogEntry now no longer has a foreign key constraint to User. The new table for User doesn't recreate those. You will have to add the missing constraints back in again manually. Either by manually updating the database or by writing a raw sql migration.
Update content type
After applying one of the three above options there's still one loose end. Django registers every model in the django_content_type table. That table contains a line for sourceapp.User. Without intervention that line will stay there as a stale row. That isn't a big problem as Django will automatically register the new destapp.User model. But it can be cleaned up by adding the following migration to rename the existing content type registration to destapp:
from django.db import migrations
# If User previously existed in sourceapp, we want to switch the content type object. If not, this will do nothing.
def change_user_type(apps, schema_editor):
ContentType = apps.get_model("contenttypes", "ContentType")
ContentType.objects.filter(app_label="sourceapp", model="user").update(
app_label="destapp"
)
class Migration(migrations.Migration):
dependencies = [
("destapp", "00xx_previous_migration_here"),
]
operations = [
# No need to do anything on reversal
migrations.RunPython(change_user_type, reverse_code=lambda a, s: None),
]
This function only works if there is no entry in django_content_type for destapp.User yet. If there is, you'll need a smarter function:
from django.db import migrations, IntegrityError
from django.db.transaction import atomic
def change_user_type(apps, schema_editor):
ContentType = apps.get_model("contenttypes", "ContentType")
ct = ContentType.objects.get(app_label="sourceapp", model="user")
with atomic():
try:
ct.app_label="destapp"
ct.save()
return
except IntegrityError:
pass
ct.delete()
I just switched out the django User model for a CustomUser model. I've got a field called contributor_id in a Project model which is a m2m field connected to the User model. I've told the m2m field to point to the CustomUser model and have run makemigrations and migrate but the user_id field has not changed to customuser_id. This is causing the following error:
Unknown column 'projects_project_contributor_id.customuser_id' in 'field list'
I found these bug reports: bug1 and bug2 but they both appear to have been fixed in 2.0. Obviously I can just run an alter table query on my database, but I don't know if:
a) There's away to get the django orm to do the change, and
b) if altering the tables directly will get my migrations out of wack.
I currently have a Django powered in-production web app that contains multiple models, sitting on top of a Postgresql database (Google Cloud SQL)
During initial set-up, one of the models was set up as follows:
class ExampleModel(models.Model):
id = models.CharField(max_length=60, unique=True, primary_key=True)
new_id = models.CharField(max_length=60, unique=True, null=True, db_index=True)
name = models.CharField(max_length=300, db_index=True)
tags = models.ManyToManyField(Tag, blank=True)
The id field contains a unique ID like: AB123456789.
I have since realised this is a mistake and would like to revert the primary key field to a standard auto-incrementing autofield, and instead use the 'new_id' field to store the unique ID.
Please can someone provide guidance on how I can make this change and perform the necessary database migrations? There are a number of foreign key fields in other models that currently use the id field in the above model which will need changing. As you can see in the above, there is also a many to many field between this model and a tag model.
I tried removing the id field from my models.py file and migrating - it initially gave an error linked to null fields and default values so I set a dummy default value in the Terminal window and removed this in the migration file.
The database removed the id field successfully and generated a new Autonumber primary key field however none of the many to many or foreign key relationships were kept post migration. I have since rolled back to a prior version of the database.
Generally this will be your approach. Steps 1-4 can be merged into a single deployment. Steps 5-7 into another. Then 8-9 would be the final.
Create new auto field.
Create new FK nullable relationships on models
Update all code that creates related models to populate both FK's
Populate all the null FK fields via a script
Make the new FK fields not-nullable
Make old FK's nullable.
Remove old FK usages from code base
Migration to remove old ID field and FKs.
(optional) Rename auto field to be ID and potentially use Django's built-in field.
I am new to Django and I am wondering about how to work with Django when it comes to an existing database
For instance, let say we have the following tables
Table Student
ID primary key
First_Name Text
Last_Name Text
Table Classes
CID primary key
SKF ForeignKey
Class Text
Ok, this database is already created and has data inside and the foreign and primary keys are already set up. Now, when creating our model, how do we tell Django the foreignkey is the SKF field? If you need any more information please let me know. Thank you for your time.
Django has a built-in function (inspectdb) for auto-generating models from an existing database.
Of course it also takes care of generating the corresponding ForeignKeys.
so before number field was uncommented i use to get this error Programmingerror: exampeuser.number has no relation to blahblah
class ExampleUser(models.Model):
#number = models.CharField(max_length=15)
phoneID = models.CharField(max_length=30)
verified = models.BooleanField(default=False)
verificationNumber = models.CharField(max_length=5)
now i comment it and syncdb and everything, it gives me an IntegrityError: null value in column "userPhone" violates not-null constraint when i try to save it in the admin website.
userPhone was a field i used a while back and changed it to number.
those errors occur everytime i try to save a model object in the admin site.
it seems that my model is still ineteracting with old changes i made previously. I am new to django.
i also have south on my installed_app and i have not yet migrated or made any configs with it. please help , this is very fustrating
It seems like you're trying to migrate your model, for example, you write a model, then you sync it, then you modify it, change some data types, delete some attributes and add some other and then sync it again.
Django does not support this by default, you need to use South(1) a library that migrates your models through this changes.
Unless you're using django 1.6, in this version they added migrations, here is the documentation. https://docs.djangoproject.com/en/dev/topics/migrations/
Whenever you need to sync a modified model, you need to do it with a migration, not with syncdb.
(1) http://south.aeracode.org/