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é.

173voto

maazza Points 1092

Une autre raison possible pour cela peut-être parce que vous essayez de définir une colonne comme NOT NULL alors qu'elle a déjà des valeurs NULL.

8 votes

Pour résoudre ce problème, vous pouvez soit utiliser une migration de données, soit mettre à jour manuellement (manage.py shell) les valeurs non conformes.

0 votes

@mgojohn Comment fais-tu cela?

1 votes

@pyramidface Si vous n'êtes pas trop pointilleux, vous pouvez simplement mettre à jour les valeurs nulles dans la coquille django. Si vous recherchez quelque chose de plus formel et testable, cela dépend des versions que vous utilisez. Si vous utilisez south, consultez : south.readthedocs.org/fr/latest/tutorial/part3.html et si vous utilisez les migrations de django, consultez la section "migrations de données" ici : docs.djangoproject.com/fr/1.8/topics/migrations

167voto

guettli Points 3284

Chaque migration est à l'intérieur d'une transaction. Dans PostgreSQL, vous ne devez pas mettre à jour la table puis modifier le schéma de la table dans une seule transaction.

Vous devez diviser la migration des données et la migration du schéma. Commencez par créer la migration des données avec ce code :

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

Ensuite, créez la migration du schéma :

manage.py schemamigration fooapp --auto

Maintenant, vous avez deux transactions et la migration en deux étapes devrait fonctionner.

12 votes

PostgreSQL a probablement modifié son comportement concernant de telles transactions, car j'ai réussi à exécuter une migration avec à la fois des modifications de données et de schéma sur ma machine de développement (PostgreSQL 9.4) alors que cela a échoué sur le serveur (PostgreSQL 9.1).

1 votes

Presque la même chose pour moi. Cela a fonctionné parfaitement pour plus de 100 migrations (dont environ 20 migrations de données) jusqu'à aujourd'hui, en ajoutant une contrainte unique ensemble avec une migration de données supprimant les doublons avant cela. PostgreSQL 10.0

2 votes

Si vous utilisez une opération RunPython dans la migration pour la migration des données, vous devez juste vous assurer que c'est la dernière opération. Django sait que si l'opération RunPython est la dernière, il ouvrira sa propre transaction.

38voto

sluge Points 340

Aux opérations, j'ai mis SET CONSTRAINTS :

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

29voto

Zags Points 582

Si vous ajoutez un champ non nul, vous devez le faire en deux migrations :

  1. AddField et RunPython pour le peupler
  2. AlterField pour changer le champ en non nul

Explication

Sur PostgreSQL et SQLite, ce problème peut survenir si vous avez une commande RunPython suffisamment complexe combinée avec des modifications de schéma dans la même migration. Par exemple, si vous ajoutez un champ non nul, les étapes de migration typiques sont les suivantes :

  1. AddField pour ajouter le champ comme nullable
  2. RunPython pour le peupler
  3. AlterField pour changer le champ en non nul

Sur SQLite et Postgres, cela peut causer des problèmes car tout est fait dans une seule transaction.
Les documents Django contiennent un avertissement spécifique à ce sujet :

Sur les bases de données prenant en charge les transactions DDL (SQLite et PostgreSQL), les opérations RunPython n'ont pas de transactions automatiquement ajoutées, en dehors des transactions créées pour chaque migration. Ainsi, sur PostgreSQL par exemple, vous devriez éviter de combiner des modifications de schéma et des opérations RunPython dans la même migration, sinon vous pourriez rencontrer des erreurs comme OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events.

Si tel est le cas, la solution est de séparer votre migration en plusieurs migrations. En général, la manière de diviser consiste à avoir une première migration contenant les étapes jusqu'à la commande run_python et une seconde migration contenant toutes celles après. Ainsi, dans le cas décrit ci-dessus, le modèle serait le AddField et RunPython dans une migration, et le AlterField dans une seconde.

8voto

clime Points 2431

Vient de rencontrer ce problème. Vous pouvez également utiliser db.start_transaction() et db.commit_transaction() lors de la migration de schéma pour séparer les modifications de données des modifications de schéma. Probablement pas aussi propre que d'avoir une migration de données séparée mais dans mon cas, j'aurais besoin de schéma, de données, et ensuite d'une autre migration de schéma donc j'ai décidé de tout faire en même temps.

8 votes

Le problème avec cette solution est le suivant : que se passe-t-il si votre migration échoue après db.commit_transaction()? Je préfère utiliser trois migrations, si vous en avez besoin : schema-mig, data-mig, schema-mig.

6 votes

Voir : django.readthedocs.io/en/latest/ref/migration-operations.htm‌​l Sur les bases de données prenant en charge les transactions DDL (SQLite et PostgreSQL), les opérations RunPython n'ont pas de transactions ajoutées automatiquement en dehors des transactions créées pour chaque migration. Ainsi, sur PostgreSQL, par exemple, vous devriez éviter de combiner des changements de schéma et des opérations RunPython dans la même migration ou vous pourriez rencontrer des erreurs comme OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events.

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