Django-DB-Migrations: impossibile ALTER TABLE perché ha eventi trigger in sospeso


121

Voglio rimuovere null = True da un TextField:

-    footer=models.TextField(null=True, blank=True)
+    footer=models.TextField(blank=True, default='')

Ho creato una migrazione dello schema:

manage.py schemamigration fooapp --auto

Poiché alcune colonne del piè di pagina contengono, NULLottengo questo errorse eseguo la migrazione:

django.db.utils.IntegrityError: la colonna "footer" contiene valori nulli

Ho aggiunto questo alla migrazione dello schema:

    for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
        sender.footer=''
        sender.save()

Adesso ottengo:

django.db.utils.DatabaseError: cannot ALTER TABLE "fooapp_emailsender" because it has pending trigger events

Che c'è?


1
Questa domanda è simile: stackoverflow.com/questions/28429933/… e aveva risposte che mi erano più utili.
SpoonMeiser

Risposte:


138

Un altro motivo potrebbe essere perché provi a impostare una colonna su NOT NULLquando in realtà ha già NULLvalori.


7
Per risolvere questo problema, puoi utilizzare una migrazione dei dati o manualmente (shell manage.py) accedere e aggiornare i valori non conformi
mgojohn

@mgojohn Come ci riesci?
pyramidface

1
@pyramidface Se non sei troppo esigente, puoi semplicemente aggiornare i valori nulli nella shell di django. Se stai cercando qualcosa di più formale e testabile, dipende dalle versioni che stai utilizzando. Se utilizzi south, consulta: south.readthedocs.org/en/latest/tutorial/part3.html e se utilizzi le migrazioni di django, consulta la sezione "migrazioni di dati" qui: docs.djangoproject.com/en/1.8/topics/ migrazioni
mgojohn

131

Ogni migrazione è all'interno di una transazione. In PostgreSQL non è necessario aggiornare la tabella e quindi alterare lo schema della tabella in una transazione.

È necessario dividere la migrazione dei dati e la migrazione dello schema. Per prima cosa crea la migrazione dei dati con questo codice:

 for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
    sender.footer=''
    sender.save()

Quindi crea la migrazione dello schema:

manage.py schemamigration fooapp --auto

Ora hai due transazioni e la migrazione in due passaggi dovrebbe funzionare.


8
PostgreSQL probabilmente ha cambiato il suo comportamento riguardo a tali transazioni, poiché sono riuscito a eseguire una migrazione con modifiche sia ai dati che allo schema sulla mia macchina di sviluppo (PostgreSQL 9.4) mentre non funzionava sul server (PostgreSQL 9.1).
Bertrand Bordage

1
Quasi lo stesso per me. Ha funzionato perfettamente per oltre 100 migrazioni (incluse ~ 20 migrazioni di dati) fino ad oggi, aggiungendo un vincolo unico insieme alla migrazione dei dati rimuovendo i duplicati prima di esso. PostgreSQL 10.0
Fan di LinPy

Se si utilizza un'operazione RunPython nella migrazione per la migrazione dei dati, è sufficiente assicurarsi che sia l'ultima operazione. Django sa che se l'operazione RunPython è l'ultima, per aprire la propria transazione.
Dougyfresh

1
@Dougyfresh è una caratteristica documentata di django?
guettli

In realtà non lo vedo da nessuna parte, era solo qualcosa che ho osservato. docs.djangoproject.com/en/2.2/ref/migration-operations/…
Dougyfresh

9

Ho appena incontrato questo problema. È inoltre possibile utilizzare db.start_transaction () e db.commit_transaction () nella migrazione dello schema per separare le modifiche ai dati dalle modifiche allo schema. Probabilmente non così pulito da avere una migrazione dei dati separata, ma nel mio caso avrei bisogno di schema, dati e poi un'altra migrazione dello schema, quindi ho deciso di farlo tutto in una volta.


7
Il problema con questa soluzione è questo: cosa succede se la migrazione non riesce dopo db.commit_transaction ()? Preferisco usare tre migrazioni, se ne hai bisogno: schema-mig, data-mig, schema-mig.
guettli

5
Vedere: django.readthedocs.io/en/latest/ref/migration-operations.html Sui database che supportano le transazioni DDL (SQLite e PostgreSQL), le operazioni RunPython non hanno alcuna transazione aggiunta automaticamente oltre alle transazioni create per ogni migrazione. Pertanto, su PostgreSQL, ad esempio, dovresti evitare di combinare modifiche allo schema e operazioni RunPython nella stessa migrazione o potresti incontrare errori come OperationalError: can't ALTER TABLE "mytable" perché ha eventi trigger in sospeso.
Iasmini Gomes

5

Alle operazioni metto VINCOLI IMPOSTATI:

operations = [
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
    migrations.RunPython(migration_func),
    migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;'),
]


0

Stai alterando lo schema della colonna. Quella colonna del piè di pagina non può più contenere un valore vuoto. Molto probabilmente ci sono valori vuoti già memorizzati nel DB per quella colonna. Django aggiornerà quelle righe vuote nel tuo DB da vuote al valore ora predefinito con il comando migrate. Django cerca di aggiornare le righe in cui la colonna del piè di pagina ha un valore vuoto e di modificare lo schema nello stesso momento in cui sembra (non sono sicuro).

Il problema è che non puoi modificare lo stesso schema di colonna per cui stai cercando di aggiornare i valori contemporaneamente.

Una soluzione sarebbe eliminare il file delle migrazioni aggiornando lo schema. Quindi, esegui uno script per aggiornare tutti quei valori al tuo valore predefinito. Quindi riesegui la migrazione per aggiornare lo schema. In questo modo, l'aggiornamento è già fatto. La migrazione di Django sta solo alterando lo schema.


1
L'esecuzione di alcuni script non è davvero un'opzione per me. Ho diverse istanze del database e il processo di distribuzione continua chiama semplicemente "manage.py migrate". Questa domanda è già risposte valide che funzionano bene.
guettli
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.