Django-DB-Migrations: невозможно ИЗМЕНИТЬ ТАБЛИЦУ, потому что у нее есть ожидающие события триггера

Я хочу удалить 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

Что не так?


person guettli    schedule 11.10.2012    source источник
comment
Этот вопрос аналогичен: stackoverflow. com/questions/28429933/ и получил ответы, которые были для меня более полезными.   -  person SpoonMeiser    schedule 25.04.2017


Ответы (7)


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

person maazza    schedule 25.02.2014
comment
Чтобы решить эту проблему, вы можете либо использовать миграцию данных, либо вручную (оболочка manage.py) войти и обновить несоответствующие значения. - person mgojohn; 27.10.2014
comment
@mgojohn Как ты это делаешь? - person pyramidface; 29.07.2015
comment
@pyramidface Если вы не слишком придирчивы, вы можете просто обновить нулевые значения в оболочке django. Если вы ищете что-то более формальное и поддающееся тестированию, это зависит от того, какие версии вы используете. Если вы используете юг, см.: south.readthedocs.org/en/latest/tutorial /part3.html и если вы используете миграцию django, см. раздел миграции данных здесь: docs.djangoproject.com/en/1.8/topics/migrations - person mgojohn; 29.07.2015
comment
Вы спасли мой день! - person Vladimir Prudnikov; 25.11.2020
comment
Я большой поклонник Django, но очевидно, что такие сообщения об ошибках — это ПОЛНАЯ красная селедка — просто показывают сложность таких платформ! - person AntonOfTheWoods; 15.02.2021

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

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

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

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

manage.py schemamigration fooapp --auto

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

person Community    schedule 11.10.2012
comment
PostgreSQL, вероятно, изменил свое поведение в отношении таких транзакций, поскольку мне удалось выполнить миграцию с изменениями как данных, так и схемы на моей машине разработки (PostgreSQL 9.4), в то время как на сервере (PostgreSQL 9.1) произошел сбой. - person Bertrand Bordage; 24.01.2015
comment
Почти то же самое для меня. Он работал безупречно для 100+ миграций (включая ~ 20 миграций данных) до сегодняшнего дня, добавляя уникальное совместное ограничение вместе с миграцией данных, удаляя дубликаты перед этим. ПостгресSQL 10.0 - person LinPy fan; 10.04.2018
comment
Если для переноса данных используется операция RunPython, вам просто нужно убедиться, что это последняя операция. Джанго знает, что если операция RunPython последняя, ​​то нужно открыть собственную транзакцию. - person Dougyfresh; 10.09.2019
comment
@Dougyfresh это задокументированная функция django? - person guettli; 11.09.2019
comment
Я на самом деле нигде этого не вижу, это было просто то, что я наблюдал. docs.djangoproject.com/en/2.2/ref/migration-operations / - person Dougyfresh; 12.09.2019
comment
@Dougyfresh страница, на которую вы ссылаетесь, содержит слово в последний раз. Но контекст другой. Речь идет о последней миграции, а не о последней операции. Тем не менее, я не понимаю этого вашего комментария. Django знает, что если операция RunPython является последней, нужно открыть свою собственную транзакцию. - person guettli; 14.09.2019
comment
Я сказал, что это просто то, что я наблюдал. Я столкнулся с той же ошибкой, с которой столкнулся OP при выполнении миграции с прибл. 12 операций с операцией RunPython посередине. Столкнувшись с этой ошибкой, я переместил операцию RunPython в последнюю операцию без каких-либо других изменений. После этого миграция прошла успешно. - person Dougyfresh; 16.09.2019
comment
Миграция Django также поддерживает Migration.atomic = False, что позволяет вам не плевать на файл (как указано в ответе). - person jnns; 28.08.2020
comment
@jnns спасибо, установка atomic на False решила мою проблему - person DataGreed; 06.11.2020
comment
Параметр atomic False запускает миграцию без транзакции? Транзакция postgresql в схеме - это хорошо, что помогает вам не использовать схему отката вручную в db. Если ответ положительный, я думаю, что это способ получить ошибки при развертывании приложения. Подойдите к ответу наилучшим выбором, и это официальный выбор, который вы можете найти в документации django. - person Max Maximov; 21.12.2020

На операции ставлю SET CONSTRAINTS:

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

Если вы добавляете необнуляемое поле, вам нужно сделать это за две миграции:

  1. AddField и RunPython, чтобы заполнить его
  2. AlterField, чтобы изменить поле, чтобы оно не допускало значения NULL

Объяснение

В PostgreSQL и SQLite эта проблема может возникнуть, если у вас есть достаточно сложная команда RunPython в сочетании с изменениями схемы в той же миграции. Например, если вы добавляете поле, не допускающее значение NULL, типичные шаги миграции для этого следующие:

  1. AddField, чтобы добавить поле как обнуляемое
  2. RunRython чтобы заполнить его
  3. AlterField чтобы изменить поле, чтобы оно не допускало значения NULL

В SQLite и Postgres это может вызвать проблемы, поскольку все делается за одну транзакцию.
Документы Django содержат специальное предупреждение об этом:

В базах данных, которые поддерживают транзакции DDL (SQLite и PostgreSQL), в операции RunPython не добавляются никакие транзакции, кроме транзакций, созданных для каждой миграции. Таким образом, в PostgreSQL, например, вам следует избегать объединения изменений схемы и операций RunPython в одной и той же миграции, иначе вы можете столкнуться с такими ошибками, как OperationalError: cannot ALTER TABLE mytable, потому что у него есть ожидающие события триггера.

В этом случае решение состоит в том, чтобы разделить вашу миграцию на несколько миграций. В общем, способ разделения состоит в том, чтобы иметь первую миграцию, содержащую шаги, выполняемые командой run_python, и вторую миграцию, содержащую все шаги после нее. Таким образом, в случае, описанном выше, шаблон будет AddField и RunPython в одной миграции и AlterField во второй.

person Zags    schedule 28.10.2020

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

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

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

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

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

person Uzzi Emuchay    schedule 23.04.2020
comment
Запуск какого-то скрипта для меня не вариант. У меня есть несколько экземпляров базы данных, и процесс непрерывного развертывания просто вызывает manage.py migrate. На этот вопрос уже есть действительные ответы, которые отлично работают. - person guettli; 28.04.2020

шаг 1) решение состоит в том, чтобы удалить последнюю миграцию из папки миграции и удалить последние добавленные поля в моделях.

шаг 2) затем снова сделать миграцию и мигрировать

Шаг 3) В последний раз снова добавьте поле, которое было удалено на первом шаге.

шаг 4) затем снова сделать миграцию и мигрировать

Проблема решена

person Shah Vipul    schedule 11.03.2021