Django-DB-Migrations: no se puede ALTERAR LA TABLA porque tiene eventos de activación pendientes

121

Quiero eliminar null = True de un TextField:

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

Creé una migración de esquema:

manage.py schemamigration fooapp --auto

Dado que algunas columnas de pie de página contienen NULL, obtengo esto errorsi ejecuto la migración:

django.db.utils.IntegrityError: la columna "pie de página" contiene valores nulos

Agregué esto a la migración del esquema:

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

Ahora obtengo:

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

¿Qué está mal?

guettli
fuente
1
Esta pregunta es similar: stackoverflow.com/questions/28429933/… y tenía respuestas que fueron más útiles para mí.
SpoonMeiser

Respuestas:

138

Otra razón para esto tal vez sea porque intentas establecer una columna NOT NULLcuando ya tiene NULLvalores.

maazza
fuente
7
Para abordar esto, puede usar una migración de datos o manualmente (manage.py shell) ingresar y actualizar los valores no compatibles
mgojohn
@mgojohn ¿Cómo haces eso?
pyramidface
1
@pyramidface Si no es demasiado exigente, puede actualizar los valores nulos en el shell de django. Si está buscando algo más formal y comprobable, depende de las versiones que esté usando. Si usa south, vea: south.readthedocs.org/en/latest/tutorial/part3.html y si usa las migraciones de django, vea la sección "migraciones de datos" aquí: docs.djangoproject.com/en/1.8/topics/ migraciones
mgojohn
131

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

Debe dividir la migración de datos y la migración de esquema. Primero cree la migración de datos con este código:

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

Luego crea la migración del esquema:

manage.py schemamigration fooapp --auto

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

guettli
fuente
8
PostgreSQL probablemente cambió su comportamiento con respecto a tales transacciones, ya que logré ejecutar una migración con cambios de datos y de esquema en mi máquina de desarrollo (PostgreSQL 9.4) mientras fallaba en el servidor (PostgreSQL 9.1).
Bertrand Bordage
1
Casi lo mismo para mí. Funcionó a la perfección para más de 100 migraciones (incluidas ~ 20 migraciones de datos) hasta el día de hoy, al tiempo que agrega restricciones únicas junto con la migración de datos eliminando los duplicados anteriores. PostgreSQL 10.0
Fan de LinPy
Si utiliza una operación RunPython en la migración para la migración de datos, solo debe asegurarse de que sea la última operación. Django sabe que si la operación RunPython es la última, debe abrir su propia transacción.
Dougyfresh
1
@Dougyfresh ¿es esta una característica documentada de django?
guettli
De hecho, no veo esto en ningún lado, fue solo algo que observé. docs.djangoproject.com/en/2.2/ref/migration-operations/…
Dougyfresh
9

Acabo de abordar 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 de esquema. Probablemente no sea tan limpio como para tener una migración de datos separada, pero en mi caso necesitaría un esquema, datos y luego otra migración de esquema, así que decidí hacerlo todo de una vez.

clima
fuente
7
El problema con esta solución es el siguiente: ¿Qué sucede si su migración falla después de db.commit_transaction ()? Prefiero usar tres migraciones, si necesita esto: schema-mig, data-mig, schema-mig.
guettli
5
Ver: django.readthedocs.io/en/latest/ref/migration-operations.html En las bases de datos que admiten transacciones DDL (SQLite y PostgreSQL), las operaciones RunPython no tienen transacciones agregadas automáticamente además de las transacciones creadas para cada migración. Por lo tanto, en PostgreSQL, por ejemplo, debe evitar combinar cambios de esquema y operaciones RunPython en la misma migración o puede encontrar errores como OperationalError: can't ALTER TABLE "mytable" porque tiene eventos de activación pendientes.
Iasmini Gomes
5

En las operaciones pongo SET CONSTRAINTS:

operations = [
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
    migrations.RunPython(migration_func),
    migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;'),
]
babosa
fuente
Es mejor usar SeparateDatabaseAndState
bdoubleu
0

Está alterando el esquema de la columna. Esa columna de pie de página ya no puede contener un valor en blanco. Lo más probable es que ya haya valores en blanco almacenados en la base de datos para esa columna. Django actualizará esas filas en blanco en su base de datos desde el espacio en blanco al valor ahora predeterminado con el comando migrate. Django intenta actualizar las filas donde la columna de pie de página tiene un valor en blanco y cambia el esquema al mismo tiempo que parece (no estoy seguro).

El problema es que no puede modificar el mismo esquema de columna para el que está intentando actualizar los valores al mismo tiempo.

Una solución sería eliminar el archivo de migraciones actualizando el esquema. Luego, ejecute un script para actualizar todos esos valores a su valor predeterminado. Luego, vuelva a ejecutar la migración para actualizar el esquema. De esta forma, la actualización ya está hecha. La migración de Django solo está alterando el esquema.

Uzzi Emuchay
fuente
1
Ejecutar algún script no es realmente una opción para mí. Tengo varias instancias de la base de datos y el proceso de implementación continua solo llama "manage.py migrate". Esta pregunta ya tiene respuestas válidas que funcionan bien.
guettli