103 votos

sur: no puede ALTERAR LA TABLA porque tiene eventos de activación pendientes

Quiero eliminar null=True de un TextField:

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

He creado una migración de esquema:

manage.py schemamigration fooapp --auto

Como algunas columnas del pie de página contienen NULL, me da este error si ejecuto la migración:

django.db.utils.IntegrityError: column "footer" contains null values

He añadido esto a la migración del esquema:

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

Ahora sí:

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

¿Qué ocurre?

122voto

maazza Puntos 1092

Otra razón para esto puede ser porque intentas establecer una columna como no nula cuando en realidad ya tiene valores NULL

114voto

guettli Puntos 3284

Cada migración está dentro de una transacción. En PostgreSQL no se debe actualizar la tabla y luego alterar el esquema de la tabla en una sola transacción.

Es necesario dividir la migración de datos y la migración de esquemas. Primero crea la migración de datos con este código:

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

A continuación, cree la migración del esquema:

manage.py schemamigration fooapp --auto

Ahora tienes dos transacciones y la migración en dos pasos debería funcionar.

25voto

sluge Puntos 429

En las operaciones pongo SET CONSTRAINTS:

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

17voto

Zags Puntos 582

Si va a añadir un campo no anulable, deberá hacerlo en dos migraciones:

  1. AddField y RunPython para rellenarlo
  2. AlterField cambiar el campo para que no sea nulo

Explaniation

En PostgreSQL y SQLite, este problema puede producirse si se dispone de un archivo RunPython combinado con alteraciones del esquema en la misma migración. Por ejemplo, si está añadiendo un campo no anulable, los pasos de migración típicos para esto son:

  1. AddField para añadir el campo como anulable
  2. RunRython para rellenarlo
  3. AlterField cambiar el campo para que no sea nulo

En SQLite y Postgres, esto puede causar problemas porque todo se hace en una sola transacción.
En Documentación sobre Django tienen una advertencia específica al respecto:

En las bases de datos que sí soportan transacciones DDL (SQLite y PostgreSQL), las operaciones RunPython no tienen ninguna transacción añadida automáticamente además de las transacciones creadas para cada migración. Así, en PostgreSQL, por ejemplo, debes evitar combinar cambios de esquema y operaciones RunPython en la misma migración o puedes encontrarte con errores como OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events.

Si este es el caso, la solución es separar la migración en varias migraciones. En general, la forma de dividir es tener una primera migración que contenga los pasos hasta el comando run_python y la segunda migración que contenga todos los posteriores. Así, en el caso descrito anteriormente, el patrón sería el siguiente AddField y RunPython en una migración, y el AlterField en un segundo.

9voto

clime Puntos 2431

Acabo de encontrarme con este problema. También puede utilizar db.start_transaction() y db.commit_transaction() en la migración del esquema para separar los cambios de datos de los cambios del esquema. Probablemente no sea tan limpio como para tener una migración de datos separada, pero en mi caso necesitaría el esquema, los datos, y luego otra migración de esquema, así que decidí hacerlo todo a la vez.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X