Django-DB-Migrations: не може змінити таблицю, оскільки вона має тривалі події тригера


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

Що не так?


1
Це питання схоже: stackoverflow.com/questions/28429933/…, і було відповіді, які були мені більш корисні.
SpoonMeiser

Відповіді:


139

Інша причина цього, можливо, тому, що ви намагаєтеся встановити стовпчик, NOT NULLколи він фактично вже має NULLзначення.


7
Для вирішення цього питання можна скористатися міграцією даних або вручну (управляти оболонками управління), увійти та оновити невідповідні значення
mgojohn

@mgojohn Як це зробити?
pyramidface

1
@pyramidface Якщо ви не надто вибагливі, можете просто оновити нульові значення в оболонці django. Якщо ви шукаєте щось більш формальне та перевірене, це залежить від того, які версії ви використовуєте. Якщо ви використовуєте на південь, см: south.readthedocs.org/en/latest/tutorial/part3.html і якщо ви використовуєте міграції Джанго, побачити «перенесення даних» розділ тут: docs.djangoproject.com/en/1.8/topics/ міграції
mgojohn

131

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

Вам потрібно розділити міграцію даних та міграцію схеми. Спочатку створіть міграцію даних за допомогою цього коду:

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

Потім створіть міграцію схеми:

manage.py schemamigration fooapp --auto

Тепер у вас є дві транзакції, і міграція в два етапи повинна працювати.


8
PostgreSQL, ймовірно, змінив свою поведінку щодо таких транзакцій, тому що мені вдалося запустити міграцію як із змінами даних, так і зі схеми на моїй машині розробки (PostgreSQL 9.4), а не на сервері (PostgreSQL 9.1).
Бертран Бордаж

1
Майже те саме для мене. Він працював бездоганно для 100+ міграцій (включаючи ~ 20 міграцій даних) до сьогодні, одночасно додаючи єдине спільне обмеження разом із міграцією даних, видаляючи дублікати перед ним. PostgreSQL 10.0
вентилятор LinPy

Якщо для операції міграції даних використовується операція RunPython, потрібно просто переконатися, що це остання операція. Django знає, що якщо операція RunPython буде останньою, відкрити власну транзакцію.
Dougyfresh

1
@Dougyfresh це документально підтверджена особливість джанго?
guettli

Я насправді цього ніде не бачу, це було лише те, що я спостерігав. docs.djangoproject.com/uk/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: не можна ВЗНАЧИТИ ТАБЛИЦЮ "mytable", оскільки вона має очікувані події тригера.
Ясміні Гомес

5

Під час операцій я поклав SET CONSTRAINTS:

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

Краще використовувати SeparateDatabaseAndState
bdoubleu

0

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

Проблема полягає в тому, що ви не можете змінити ту саму схему стовпців, для якої намагаєтесь одночасно оновити значення.

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


1
Запуск якогось сценарію для мене насправді не є варіантом. У мене є кілька екземплярів бази даних, і процес безперервного розгортання просто викликає "Manag.py migrate". На це запитання вже є відповіді, які добре працюють.
guettli
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.