Issue
I have a model SessionCategory
with a unique field name
. Certain pivotal instances of this model are referenced by name
; the only problem is that name
is also editable in our internal dashboard so that a user could inadvertently 'break' these references by changing the name
.
To solve this, I'd like to create a new field name_slug
which is a slugified version of name
, also with a unique constraint. I've tried to do the following:
from django.db import models
from django.utils.text import slugify
from django.db.models.signals import pre_save
from django.dispatch import receiver
class SessionCategory(models.Model):
name = models.CharField(max_length=255, unique=True)
name_slug = models.CharField(max_length=255, unique=True)
@receiver(pre_save, sender=SessionCategory)
def create_name_slug(sender, instance, **kwargs):
if not instance.name_slug:
instance.name_slug = slugify(instance.name)
where I've added the name_slug
unique field. The problem is that if I try to python manage.py makemigrations
, I get the following prompt:
(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py makemigrations
You are trying to add a non-nullable field 'name_slug' to sessioncategory 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
Select an option:
I've come across a workflow at https://docs.djangoproject.com/en/dev/howto/writing-migrations/#migrations-that-add-unique-fields for writing migrations that create unique fields. In that example, the unique field is generated using the 'general' function uuid.uuid4()
. In my case, however, I need access to the name
of the specific instance, like I was hoping to obtain by connecting to the pre_save
signal.
How can I create this migration and maintain the unique constraint?
Solution
I think the RunPython
feature will help you.
Step1. You have already a migration file before adding name_slug
field. Then create a new migration file with, name_slug = models.CharField(max_length=255, unique=True,null=True)
.The file will be like this
app_name/migrations/0003_some_name.py
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sample', '0006_sessioncategory'),
]
operations = [
migrations.AddField(
model_name='sessioncategory',
name='name_slug',
field=models.CharField(max_length=255, null=True, unique=True),
),
]
**Step2.** Add a custom migration scrip by using `RunPython` as below,
from __future__ import unicode_literals
from django.db import migrations, models
from django.utils.text import slugify
class Migration(migrations.Migration):
def forwards_func(apps, schema_editor):
SessionCategory = apps.get_model("sample", "SessionCategory")
for instance in SessionCategory.objects.all():
if not instance.name_slug:
instance.name_slug = slugify(instance.name)
instance.save()
def reverse_func(apps, schema_editor):
pass</b>
dependencies = [
('sample', '0006_sessioncategory'),
]
operations = [
migrations.AddField(
model_name='sessioncategory',
name='name_slug',
field=models.CharField(max_length=255, null=True, unique=True),
),
<b>migrations.RunPython(forwards_func, reverse_func
)</b>
]</code></pre>
Step 3. do the migration by python manage.py migrate
That's it !
Warning!
do not run python manage.py migrate
command on step 1 (only makemigrations command)
Note
The migrations files are generated in my local system to reproduce the behaviour.It maybe different for you
Answered By - JPG
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.