I'm trying to add slug so that the title of a post appears in the url but i get this message on console:
You are trying to add a non-nullable field 'slug' to post without a default; we can't do that 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
models.py
class Post(models.Model):
title = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = self.slug or slugify(self.title)
super().save(*args, **kwargs)
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:
null=True
or without unique=True
and with some default valueAll 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.
null=True
or without unique=True
and with some default valueYou 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.
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).
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.