Django-DB-Migrations: невозможно ALTER TABLE, потому что у него есть ожидающие триггерные события

122

Я хочу удалить null = True из TextField:

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

Я создал миграцию схемы:

manage.py schemamigration fooapp --auto

Поскольку некоторые столбцы нижнего колонтитула содержат, NULLя получаю это, errorесли запускаю миграцию:

django.db.utils.IntegrityError: столбец "нижний колонтитул" содержит нулевые значения

Я добавил это в миграцию схемы:

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

Теперь я получаю:

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

Что случилось?

guettli
источник
1
Этот вопрос похож на stackoverflow.com/questions/28429933/…, и я получил более полезные для меня ответы.
SpoonMeiser

Ответы:

139

Другая причина для этого может быть связана с тем, что вы пытаетесь установить столбец, NOT NULLкогда он на самом деле уже имеет NULLзначения.

maazza
источник
7
Чтобы решить эту проблему, вы можете либо использовать миграцию данных, либо вручную (оболочка manage.py) войти и обновить несовместимые значения
mgojohn
@mgojohn Как ты это делаешь?
pyramidface
1
@pyramidface Если вы не слишком разборчивы, вы можете просто обновить нулевые значения в оболочке django. Если вы ищете что-то более формальное и тестируемое, это зависит от того, какие версии вы используете. Если вы используете юг, см .: south.readthedocs.org/en/latest/tutorial/part3.html, а если вы используете миграции django, см. Раздел «миграции данных» здесь: docs.djangoproject.com/en/1.8/topics/ migrations
mgojohn
131

Каждая миграция происходит внутри транзакции. В PostgreSQL вы не должны обновлять таблицу, а затем изменять схему таблицы за одну транзакцию.

Вам нужно разделить миграцию данных и миграцию схемы. Сначала создайте миграцию данных с помощью этого кода:

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

Затем создайте миграцию схемы:

manage.py schemamigration fooapp --auto

Теперь у вас есть две транзакции, и миграция в два этапа должна работать.

guettli
источник
8
PostgreSQL, вероятно, изменил свое поведение в отношении таких транзакций, так как мне удалось запустить миграцию с изменениями как данных, так и схемы на моем компьютере разработчика (PostgreSQL 9.4), в то время как он потерпел неудачу на сервере (PostgreSQL 9.1).
Бертран Бордаж
1
Почти то же самое для меня. До сегодняшнего дня он работал безупречно для 100+ миграций (включая ~ 20 миграций данных), добавляя уникальное ограничение вместе с миграцией данных, удаляя дубликаты до этого. PostgreSQL 10.0
фанат LinPy
Если вы используете операцию RunPython в миграции для миграции данных, вам просто нужно убедиться, что это последняя операция. Django знает, что, если операция RunPython будет последней, необходимо открыть собственную транзакцию.
Dougyfresh
1
@Dougyfresh - это задокументированная функция django?
Guettli
Я на самом деле нигде этого не вижу, я просто заметил это. docs.djangoproject.com/en/2.2/ref/migration-operations/…
Dougyfresh
9

Только что ударил эту проблему. Вы также можете использовать db.start_transaction () и db.commit_transaction () при миграции схемы, чтобы отделить изменения данных от изменений схемы. Наверное, не так чисто, чтобы иметь отдельную миграцию данных, но в моем случае мне понадобятся схема, данные, а затем еще одна миграция схемы, поэтому я решил сделать все это сразу.

климат
источник
7
Проблема с этим решением заключается в следующем: что произойдет, если миграция завершится неудачно после db.commit_transaction ()? Я предпочитаю использовать три миграции, если вам это нужно: schema-mig, data-mig, schema-mig.
guettli
5
См .: django.readthedocs.io/en/latest/ref/migration-operations.html В базах данных, которые действительно поддерживают транзакции DDL (SQLite и PostgreSQL), в операции RunPython не добавляются автоматически какие-либо транзакции, кроме транзакций, созданных для каждой миграции. Таким образом, в PostgreSQL, например, вам следует избегать объединения изменений схемы и операций RunPython в одной миграции, иначе вы можете столкнуться с ошибками, такими как OperationalError: cannot ALTER TABLE «mytable», потому что у нее есть отложенные триггерные события.
Iasmini Gomes 01
6

На свои операции ставлю УСТАНОВКИ:

operations = [
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
    migrations.RunPython(migration_func),
    migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;'),
]
sluge
источник
Лучше использовать SeparateDatabaseAndState
bdoubleu
1

Вы изменяете схему столбца. Столбец нижнего колонтитула больше не может содержать пустое значение. Скорее всего, в БД для этого столбца уже хранятся пустые значения. Django собирается обновить эти пустые строки в вашей БД с пустых до значения по умолчанию с помощью команды migrate. Django пытается обновить строки, в которых столбец нижнего колонтитула имеет пустое значение, и одновременно изменить схему (я не уверен).

Проблема в том, что вы не можете изменить ту же схему столбца, для которой вы пытаетесь обновить значения одновременно.

Одним из решений может быть удаление файла миграции, обновляющего схему. Затем запустите сценарий, чтобы обновить все эти значения до значения по умолчанию. Затем повторно запустите миграцию, чтобы обновить схему. Таким образом, обновление уже выполнено. Миграция Django только меняет схему.

Уззи Эмучай
источник
1
Запуск сценария для меня не вариант. У меня есть несколько экземпляров базы данных, и процесс непрерывного развертывания просто вызывает «manage.py migrate». На этот вопрос уже есть действительные ответы, которые работают нормально.
Guettli