158 votes

Django-DB-Migrations: impossible de MODIFIER LA TABLE car elle a des événements de déclenchement en attente

Je veux supprimer null=True d'un TextField :

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

J'ai créé une migration de schéma :

manage.py schemamigration fooapp --auto

Étant donné que certaines colonnes de footer contiennent NULL, j'obtiens cette erreur si je lance la migration :

django.db.utils.IntegrityError: la colonne "footer" contient des valeurs nulles

J'ai ajouté ceci à la migration de schéma :

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

Maintenant j'obtiens :

django.db.utils.DatabaseError: impossible de MODIFIER LA TABLE "fooapp_emailsender" car elle a des événements de déclenchement en attente

Qu'est-ce qui ne va pas ?

2 votes

Cette question est similaire : stackoverflow.com/questions/28429933/… et les réponses étaient plus utiles pour moi.

1 votes

J'ai rencontré le même problème avec Postgres v10 (mais pas avec Postgres v.12). Problème résolu, en ajoutant un fichier de migration séparé.

7voto

Antwane Points 7923

La réponse de @Zags est correcte, cette erreur survient généralement lorsque vous mélangez l'altération du schéma et la manipulation des données dans la même migration, ce qui correspond à une transaction unique par défaut.

Une autre solution pour résoudre ce problème est de définir manuellement atomic = False dans la classe Migration. Ainsi, aucune transaction n'est créée pour exécuter les opérations, chacune est exécutée séquentiellement et immédiatement appliquée à la base de données.

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('application', '00XX_migration_précédente'),
    ]

    operations = [
        migrations.AddField(
            model_name='foo',
            name='bar',
            field=models.CharField(max_length=255),
        ),
        RunPython(migrate_data, reverse_code=reverse_migrate_data, atomic=True),
        migrations.RemoveField(
            model_name='foo',
            name='baz',
        ),
    ]

Conseil : Si l'opération RunPython effectue plusieurs save(), vous pouvez l'appeler avec atomic=True pour créer une transaction pour toute l'opération.

3voto

Damir Nafikov Points 25

Dans mon cas j'ai

  1. AddField
  2. RunPython
  3. RemoveField

Ensuite, j'ai simplement déplacé le dernier RemoveField vers le nouveau fichier de migration, ce qui a résolu le problème

2voto

Uzzi Emuchay Points 49

Vous modifiez le schéma de la colonne. Cette colonne de pied de page ne peut plus contenir de valeur vide. Il est probable qu'il y ait déjà des valeurs vides stockées dans la DB pour cette colonne. Django va mettre à jour ces lignes vides dans votre DB de vide à la valeur par défaut actuelle avec la commande migrate. Django essaie de mettre à jour les lignes où la colonne de pied de page a une valeur vide et de changer le schéma en même temps apparemment (je ne suis pas sûr).

Le problème est que vous ne pouvez pas modifier en même temps le schéma de colonne que vous essayez de mettre à jour les valeurs.

Une solution serait de supprimer le fichier de migrations mettant à jour le schéma. Ensuite, exécutez un script pour mettre à jour toutes ces valeurs à votre valeur par défaut. Ensuite, relancez la migration pour mettre à jour le schéma. De cette façon, la mise à jour est déjà effectuée. La migration Django ne fait que modifier le schéma.

0voto

Shah Vipul Points 156

étape 1) la solution est de supprimer la dernière migration du dossier des migrations et de supprimer les derniers champs ajoutés dans les modèles.

étape 2) ensuite refaire la migration et migrer

étape 3) Enfin, ajoutez à nouveau le champ qui a été supprimé à la première étape

étape 4) ensuite refaire la migration et migrer

Problème résolu

0voto

Wes Kendall Points 96

La cause probable de cela est enracinée dans votre modèle ayant des contraintes de vérification via l'option Meta.constraints. Les contraintes de vérification (dans Postgres) sont implémentées sous forme de déclencheurs qui s'exécutent à la fin d'une transaction. Par défaut, un fichier de migration Django englobe toutes les opérations de migration à partir d'un fichier dans une seule transaction. Cela signifie que d'autres opérations ALTER TABLE peuvent se produire avant que vos déclencheurs ne se déclenchent, une situation que Postgres ne peut pas gérer.

Pour contourner ce problème, vous pouvez faire comme le suggère une autre réponse:

operations = [
    migrations.RemoveField(...). # Effectuez vos opérations de table normales
    migrations.RunPython(migration_func),
    # Évaluer les contraintes de vérification
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
    migrations.RemoveField(...). # Exécutez le reste de vos opérations de table
]

Exécuter 'SET CONSTRAINTS ALL IMMEDIATE;' après la migration des données garantit que toutes ces contraintes de vérification qui attendent normalement jusqu'à la fin de la transaction se déclencheront, ce qui signifie qu'il n'y a plus d'événements de déclenchement en attente avant les prochaines déclarations ALTER TABLE.

FYI - Ce paramètre est efficace uniquement pour le lanceur de migration Django dans cette transaction. Il n'affectera pas les autres sessions de base de données ou les fichiers de migration ultérieurs.

Contrairement à l'autre réponse liée, j'éviterais d'exécuter un migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;') supplémentaire, sinon vous pourriez altérer le comportement d'autres déclencheurs dans les étapes ultérieures qui devraient s'exécuter immédiatement (c'est-à-dire des déclencheurs d'application qui suivent l'historique, etc., qui ne sont pas des contraintes de vérification).

Je voulais juste ajouter un peu plus de clarté ici sur exactement pourquoi cela se produit car c'est une question plus ancienne avec de nombreuses réponses différentes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X