Django can't add a new field by itself without null
values being allowed here, especially when you have unique=True
set on this field. To solve that issue, you have to perform it in steps:
- Add a column with
null=True
or without unique=True
and with some default value
- Make sure that all records in database will have a unique value.
- Change the field to the final state.
All of those operations you can do with 3 migrations. Below are the detailed steps to do it. Before proceeding, make sure to remove all migrations created in previous attempts to solve that issue. You may have to undo those migrations from environments on which you were able to successfully apply them.
1. Add a column with null=True
or without unique=True
and with some default value
You can let Django create this migration for you. Simply edit your field to look like:
slug = models.SlugField(unique=True, null=True)
And run ./manage.py makemigrations
after doing that.
2. Make sure that all records in database will have a unique value.
This step has to be crafted by hand to some extent. Start with asking Django to create new, empty migration for you by invoking ./manage.py makemigrations YOUR_APP_NAME --empty
. Now open this new migration file and add this code (adjusted accordingly to your needs, especially make sure to generate unique values for every single record) before the Migration
class:
def populate_posts_slug_field(apps, schema_editor):
for post in apps.get_model("YOUR_APP_NAME", "post").objects.all():
post.slug = # generate a unique value for every post here
post.save()
Now, add an operation that will run code defined above when executing this migration. To do that, add this entry into the operations
list in Migration
class of your migration:
migrations.RunPython(populate_posts_slug_field, migrations.RunPython.noop),
(2nd argument for RunPython
is a special function that just does absolutely nothing, it will be executed when you want to unapply this migration).
3. Change the field to the final state.
This also can be handled by Django itself. Change your field to the final state (as in your question) and run ./manage.py makemigrations
once again.
You're all set. Running ./manage.py migrate
should succeed now.
Note: it is possible to run all 3 operations in single migration file, but it should be avoided. Running both data and schema changes in one migration can cause problems on some database backends, so it should be avoided entirely.