I added a new field to one of my models:
class Agency(models.Model):
email = models.EmailField(unique=True, verbose_name=_("e-mail"))
As this field cannot be blank, django-admin makemigrations
requested me to provide one-off default, which I did. Here is the generated migration:
# Generated by Django 1.9.4 on 2016-03-20 10:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0008_auto_20160226_1226'),
]
operations = [
migrations.AddField(
model_name='agency',
name='email',
field=models.EmailField(default='[email protected]', max_length=254, unique=True, verbose_name='e-mail'),
preserve_default=False,
),
]
As expected, django-admin migrate
throwed an error:
psycopg2.IntegrityError: could not create unique index "accounts_agency_email_key"
DETAIL: Key (email)=([email protected]) is duplicate.
I thought I could edit the migration to set unique values before making the field unique. So I tried:
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-03-20 10:38
from __future__ import unicode_literals
from django.db import migrations, models
from django.utils.text import slugify
def set_email(apps, schema_editor):
Agency = apps.get_model('accounts', 'Agency')
for agency in Agency.objects.all():
agency.email = '{}@example.fr'.format(slugify(agency.name))
agency.save()
class Migration(migrations.Migration):
dependencies = [
('accounts', '0008_auto_20160226_1226'),
]
operations = [
migrations.AddField(
model_name='agency',
name='email',
field=models.EmailField(default='', max_length=254, blank=True, verbose_name='e-mail'),
preserve_default=False,
),
migrations.RunPython(set_email),
migrations.AlterField(
model_name='agency',
name='email',
field=models.EmailField(max_length=254, unique=True, verbose_name='e-mail'),
preserve_default=False,
),
]
Unfortunately I get this error when running django-admin migrate
:
django.db.utils.OperationalError: cannot ALTER TABLE "accounts_agency" because it has pending trigger events
My guess is that operations
are not executed synchronously.
I think I could fix the issue by splitting the migration into two migrations, but I'd like to know if I can do it in only one migration. What is the common way to create migrations when adding a new unique field in a model?
PS: I also tried to use an F expression as default (default=models.F('name') + '@example.fr'
) but it failed:
django.db.utils.IntegrityError: could not create unique index "accounts_agency_email_key"
DETAIL: Key (email)=(F(name) + Vallu(@example.fr)) is duplicated.
Maybe it's too late but maybe it could work for someone else
You can do this in one migration via using migrations.RunSQL method
For your example code after you added the new field to your model and run the python manage.py makemigrations command (here if you have existing rows in your table command wants to choice default value you can choice "Provide a one-off default now" option and give some string value it is not important because actually we did not use it) then go to migration file and change operations part with this (Note i use postgresql you can change SQL for your database)
operations = [
migrations.RunSQL(
'ALTER TABLE "agency" ADD COLUMN "email" varchar(254) NULL;ALTER TABLE "agency" ALTER COLUMN "email" DROP DEFAULT;COMMIT;',
),
migrations.RunSQL(
"UPDATE agency SET email= Concat(country_code, '@example.fr');COMMIT;",
),
migrations.RunSQL(
'ALTER TABLE "agency" ALTER COLUMN "email" SET NOT NULL;ALTER TABLE "agency" ADD CONSTRAINT "agency_email_b551ad2a_uniq" UNIQUE ("email");ALTER TABLE "agency" ALTER COLUMN "email" DROP DEFAULT;CREATE INDEX "agency_email_b551ad2a_like" ON "agency" ("email" varchar_pattern_ops);COMMIT;'
)
]
then run "python manage.py migrate" command that is it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With