Миграции Peewee меняют ограничение внешнего ключа - PullRequest
0 голосов
/ 28 июня 2018

У меня есть ситуация, когда мне нужно изменить ограничение внешнего ключа для реализации каскадного удаления и написать шаги миграции для изменения. Я использую Flask с peewee в качестве инструмента ORM и peewee_migrate как инструмент для миграции.

При создании миграции из шаблона миграции, предоставленного в маршрутизаторе peewee_migrate, в миграцию добавляется набор доступных методов.

"""Peewee migrations -- 007_Added_cascade_delete.py.

Some examples (model - class or model name)::

    > Model = migrator.orm['model_name']            # Return model in current state by name

    > migrator.sql(sql)                             # Run custom SQL
    > migrator.python(func, *args, **kwargs)        # Run python code
    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
    > migrator.remove_model(model, cascade=True)    # Remove a model
    > migrator.add_fields(model, **fields)          # Add fields to a model
    > migrator.change_fields(model, **fields)       # Change fields
    > migrator.remove_fields(model, *field_names, cascade=True)
    > migrator.rename_field(model, old_field_name, new_field_name)
    > migrator.rename_table(model, new_table_name)
    > migrator.add_index(model, *col_names, unique=False)
    > migrator.drop_index(model, *col_names)
    > migrator.add_not_null(model, *field_names)
    > migrator.drop_not_null(model, *field_names)
    > migrator.add_default(model, field_name, default)

"""

Я попытался удалить столбец с помощью метода remove_fields, а затем добавить новый столбец внешнего ключа с помощью каскадного удаления с помощью метода add_fields, но безуспешно, потому что столбец не удаляется, но ошибки не отображаются, и это приводит к ошибке, которая внешний ключ с таким именем уже существует.

При использовании метода change_fields я получаю сообщение об ошибке, что drop_foreign_key_constraint не реализован в игровой среде peewee migrate, потому что метод change_fields пытается удалить внешний ключ перед изменением поля, и если измененное поле также является внешним ключом, пытается добавить ограничение внешнего ключа с помощью метода add_foreign_key_constraint (также не реализовано)

@operation
def drop_foreign_key_constraint(self, table, column_name):
    raise NotImplementedError

@operation
def add_foreign_key_constraint(self, table, column_name, field):
    raise NotImplementedError

Единственное решение, которое я придумал, - это использование метода sql, который позволяет выполнять пользовательский sql, но проблема в том, что это может работать только на конкретной базе данных из-за разного синтаксиса между postgresql, mysql и sqlite. База данных, используемая для разработки / производства, является postgres, а база данных sqlite в памяти используется для тестирования, что приводит к сбою всех тестов, но миграция завершается успешно при разработке / производстве, поэтому это «обходной путь».

if current_app.config["TESTING"] != True:
    def migrate(migrator, database, fake=False, **kwargs):
        """Write your migrations here."""

        migrator.sql('alter table {} drop constraint {};'.format(TABLE_NAME, CONSTRAINT_NAME))

        migrator.sql('alter table {} add constraint {} foreign key (device_id) references device(device_id) on delete cascade;'.format(TABLE_NAME, CONSTRAINT_NAME))


    def rollback(migrator, database, fake=False, **kwargs):
        """Write your rollback migrations here."""

        migrator.sql('alter table {} drop constraint {};'.format(TABLE_NAME, CONSTRAINT_NAME))

        migrator.sql('alter table {} add constraint {} foreign key (device_id) references device(device_id);'.format(TABLE_NAME, CONSTRAINT_NAME))
else:
    def migrate(migrator, database, fake=False, **kwargs):
        """Write your migrations here."""

    def rollback(migrator, database, fake=False, **kwargs):
        """Write your rollback migrations here."""

Поскольку sqlite не поддерживает alter table drop constraint, я понимаю, что вам нужно создать копию таблицы, вставить все данные из исходной таблицы в копию, а затем удалить исходную таблицу и переименовать таблицу копирования, но я думаю, что это излишество.

Можете ли вы предложить мне лучший способ сделать это? Могу ли я сделать это с peewee_migrate как-нибудь без использования пользовательских sql, чтобы peewee мог быть независимым от базы данных?

1 Ответ

0 голосов
/ 28 июня 2018

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

Я ничего не знаю о "peewee_migrate", но сам peewee поставляется с расширением миграции. Peewee предоставляет независимый от базы данных API для выполнения обычных операций (добавление / удаление / переименование столбцов, ограничения и т. Д.), Но из-за ограничений в типах операций, поддерживаемых sqlite, реализация миграции sqlite вынуждена делать некоторые обходные пути.

Например, sqlite может добавлять столбцы, но не может их удалять. Так что peewee делает, когда вы удаляете столбец, переименовывает таблицу, воссоздает таблицу без столбца, копирует данные и удаляет старую копию.

К сожалению, sqlite не поддерживает добавление ограничений после создания таблицы, поэтому в вашем случае лучше всего удалить столбец, а затем повторно добавить его.

Я написал тестовый пример, показывающий, как это может работать, и проверил, что он работает правильно:

https://github.com/coleifer/peewee/blob/7e61d86bf6c3f256d09b2a3e1897693dfd68b48d/tests/migrations.py#L665-L715

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...