Подтвердить что ты не робот

SQLAlchemy StaleDataError при удалении элементов, вставленных через ORM sqlalchemy.orm.exc.StaleDataError

У меня возникла проблема с получением такой ошибки:

"MyPyramidApplication Error"<class 'sqlalchemy.orm.exc.StaleDataError'>: DELETE statement on table 'page_view' expected to delete 6 row(s); Only 0 were matched.

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

У меня есть модель page_view, у которой есть внешний ключ на page_id и user_id.

Вот как выглядит модель:

page_view_table = sa.Table(
   'page_view',
    metadata,
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('page_id', sa.Integer, sa.ForeignKey('guide.id')),
    sa.Column('user_id', sa.Integer, sa.ForeignKey('user.id')),
    sa.Column('last_view', sa.DateTime, nullable=False),
    sa.UniqueConstraint('user_id', 'page_id'),
    mysql_engine='InnoDB',
    mysql_charset='utf8mb4'
)

Здесь отношения выглядят как

orm.mapper(Page, page_table,
    properties = {
        'users_viewed': sa.orm.relation(
            User,
            secondary=page_view_table,
            backref='page'),
    }
)

Я добавляю некоторые элементы в свою базу данных, используя инструкцию insert, что-то похожее на это:

ins = model.page_view_table.insert()
sql = str(ins)
sql += ' ON DUPLICATE KEY UPDATE last_view = :last_view'
session = model.Session()
session.execute(sql, page_views)
mark_changed(session)

Насколько я могу судить по журналам, транзакции передаются правильно, и я вижу элементы в БД.

Однако, когда я пытаюсь удалить элемент страницы с помощью ORM, я получаю исключение StaleDataError. Глядя на журналы, я вижу ORM, выдающий инструкцию delete, но затем откатываясь из-за ошибки.

Я попытался поэкспериментировать с session.expire_all(), а также session.expunge_all() сразу после инструкции insert, но они были не очень полезны, и я все еще ошибка.

Вот что я вижу в журналах SQLAlchemy.

2011-11-05 18:06:08,031 INFO  [sqlalchemy.engine.base.Engine][worker 3] DELETE FROM page_view WHERE page_view.page_id = %s AND page_view.user_id = %s
2011-11-05 18:06:08,031 INFO  [sqlalchemy.engine.base.Engine][worker 3] (13818L, 259L)
2011-11-05 18:06:08,032 INFO  [sqlalchemy.engine.base.Engine][worker 3] DELETE FROM page_view WHERE page_view.page_id = %s AND page_view.user_id = %s
2011-11-05 18:06:08,033 INFO  [sqlalchemy.engine.base.Engine][worker 3] (13818L, 259L)
2011-11-05 18:06:08,033 INFO  [sqlalchemy.engine.base.Engine][worker 3] ROLLBACK

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

4b9b3361

Ответ 1

Думаю, я могу дать подсказку по этой проблеме. Короткий вариант: "Вам, вероятно, придется вручную модифицировать данные в базе данных для решения проблемы".

Более длинная версия: у меня была аналогичная проблема с SQLite. У меня была следующая таблица:

ingredients = Table('ingredients', metadata,
    Column('recipe_title', Unicode, ForeignKey('recipes.title'), primary_key=True),
    Column('product_title', Unicode, ForeignKey('products.title'), primary_key=True),
    Column('amount', Integer, nullable=False),
    Column('unit_title', Unicode, ForeignKey('units.title')))

см. этот составной первичный ключ? Мне как-то удалось вставить две строки с одной и той же парой recipe_title/product_title. Я был удивлен, узнав, что на стороне SQLite нет ни одного ограничения для этой таблицы (нет первичного ключа, без ключа fereign - это просто обычная таблица ванили), но хорошо - вот путь sqlalchemy, а не мой бизнес.

Затем, когда я попытался удалить персидский объект с этими двумя строками, sqlalchemy увидел, что ограничения были нарушены, и он выбросил 'StaleDataError'. Наконец, мне просто пришлось удалить одну строку duplicatinng вручную из таблицы SQLite.

Ответ 2

Хотя столбцы могут быть помечены как primary_key, убедитесь, что это принудительно применяется и на уровне базы данных (например, когда база данных была создана другим инструментом). В MySQL это означает, что они PRIMARY KEY и не только KEY.

В моем случае было 2 столбца, помеченные как primary_key (составной), но было несколько строк, содержащих один и тот же (предположительно) уникальный id.