2016-03-20 15 views
9

ho aggiunto un nuovo campo a uno dei miei modelli:Come fare correttamente le migrazioni quando si aggiunge un nuovo campo unico

class Agency(models.Model): 
    email = models.EmailField(unique=True, verbose_name=_("e-mail")) 

Poiché questo campo non può essere vuoto, django-admin makemigrations mi ha chiesto di fornire una tantum default, che L'ho fatto. Ecco la migrazione generato:

# 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, 
     ), 
    ] 

Come previsto, django-admin migrate gettò un errore:

psycopg2.IntegrityError: could not create unique index "accounts_agency_email_key" 
DETAIL: Key (email)=([email protected]) is duplicate. 

pensavo di poter modificare la migrazione per impostare valori unici prima di fare il campo univoco. Così ho provato:

# -*- 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, 
     ), 
    ] 

Purtroppo ottengo questo errore durante l'esecuzione django-admin migrate:

django.db.utils.OperationalError: cannot ALTER TABLE "accounts_agency" because it has pending trigger events 

mia ipotesi è che operations non vengono eseguiti in modo sincrono.

Penso di poter risolvere il problema suddividendo la migrazione in due migrazioni, ma mi piacerebbe sapere se posso farlo in una sola migrazione. Qual è il modo comune per creare migrazioni quando si aggiunge un nuovo campo univoco in un modello?

PS: Ho anche cercato di usare un'espressione F di default (default=models.F('name') + '@example.fr'), ma è venuto a mancare:

django.db.utils.IntegrityError: could not create unique index "accounts_agency_email_key" 
DETAIL: Key (email)=(F(name) + Vallu(@example.fr)) is duplicated. 
+3

Hai letto la sezione di documenti che si occupa esattamente di questo problema? https://docs.djangoproject.com/en/1.9/howto/writing-migrations/#migrations-that-add-unique-fields – koniiiik

+0

@koniiiik, cerco sulla pagina sbagliata https://docs.djangoproject.com/ en/1.9/topics/migrations/Quindi, sembra che non ci sia modo di farlo funzionare con una sola migrazione. –

+1

C'è qualche ragione per cui non vuoi usare due migrazioni? – koniiiik

risposta

2

Forse è troppo tardi, ma forse potrebbe lavorare per qualcun altro

È possibile farlo in una migrazione via utilizzando migrations.RunSQL metodo

per il vostro codice di esempio dopo aver aggiunto il nuovo campo al modello ed eseguire i makemigrations manage.py pitone Comando (qui se hai righe esistenti nel tuo comando di tabella vuoi scegliere il valore predefinito puoi scegliere l'opzione "Fornisci un valore predefinito ora" e dare un valore stringa non è importante perché in realtà non l'abbiamo usato) poi vai a file di migrazione e cambiare le operazioni di parte con questo (nota che ho usare PostgreSQL è possibile modificare SQL per il 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;' 
     ) 
    ] 

quindi eseguire "pitone manage.py migrano" comando che è.

+0

Questo è stato fantastico. Grazie mille – Martin