https://stackoverflow.com/questions/60191615/how-to-modify-a-models-whos-already-migrated-in-database
To elaborate on my comment above...
Adding a new non-nullable ForeignKey in Django is generally a three-step process.
- First, you add the new ForeignKey to your model definition with
null=True
, and runmakemigrations
. This will create a migration that will add the field, nothing special about it. Executing this migration will add a column with all rows havingNULL
as the value. - Second, you create a new empty migration for the same app (
makemigrations --empty
), then edit that migration to contain a data migration step. This is where you'll need to, according to your business logic, choose some value for the new foreign key. - Third, you modify the ForeignKey in your model definition to set
null=False
and create a third migration withmakemigrations
. Django will ask whether you've dealt withnull
s somehow – you need to say that "yep, I swear I have" (since you did, above in step 2).
In practice, for a simplified version of OP's question where we'll want to add an User foreign key:
Original state
class Post(models.Model):
name = models.CharField(max_length=100)
1a. Add nullable field.
class Post(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE))
1b. Run makemigrations.
$ python manage.py makemigrations
Migrations for 'something':
something/migrations/0002_post_author.py
- Add field author to post
2a. Create a new empty migration.
$ python manage.py makemigrations something --empty -n assign_author
Migrations for 'something':
something/migrations/0003_assign_author.py
2b. Edit the migration file.
More information on data migrations can be found, as always, in the manual.
from django.db import migrations
def assign_author(apps, schema_editor):
User = apps.get_model('auth', 'User') # or whatever is your User model
Post = apps.get_model('something', 'Post') # or wherever your Post model is
user = User.objects.filter(is_superuser=True).first() # Choose some user...
assert user # ... and ensure it exists...
Post.objects.all().update(author=user) # and bulk update all posts.
class Migration(migrations.Migration):
dependencies = [...]
operations = [
migrations.RunPython(assign_author, migrations.RunPython.noop),
]
3a. Make the field non-nullable.
class Post(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(User, null=False, on_delete=models.CASCADE))
3b. Run Makemigrations.
Answer truthfully to the question – you've just added a RunPython operation.
$ python manage.py makemigrations something -n post_author_non_null
You are trying to change the nullable field 'author' on something. to non-nullable 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) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)
3) Quit, and let me add a default in models.py
Select an option: 2
Migrations for 'something':
something/migrations/0004_post_author_non_null.py
- Alter field author on post
All done!
Running migrate
will now run these three migrations and your model will have author
without data loss.
No comments:
Post a Comment