SQLAlchemy 删除通过 ORM 插入项目时的 StaleDataError

15 投票
2 回答
10763 浏览
提问于 2025-04-17 05:44

我遇到了一个问题,出现了这样的错误:

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

我大致知道问题出在哪里,但一直没能解决。

我有一个页面浏览模型,这个模型里有两个外键,一个是 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'),
    }
)

我正在通过插入语句往数据库里添加一些项目,类似这样的:

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发出了删除语句,但由于错误又回滚了。

我尝试在插入语句后使用 session.expire_all()session.expunge_all(),但这些都没什么帮助,错误依然存在。

这是我在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关系配置不当,但我觉得不是这个原因。

2 个回答

2

虽然可以把某些列标记为 primary_key,但一定要确保在数据库层面也要这样设置(比如当数据库是通过其他工具创建的时候)。在 MySQL 中,这意味着要确保它们是 PRIMARY KEY,而不仅仅是 KEY

在我的情况下,有两个列被标记为 primary_key(复合主键),但却有多行包含相同的(本该是唯一的) id

7

我想我可以给这个问题一个提示。简单来说就是:“你可能需要手动修改数据库里的数据来解决这个问题。”

稍微详细一点说:我之前在使用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里没有任何约束(没有主键,也没有外键——就只是一个普通的表),不过这也是sqlalchemy的处理方式,跟我没关系。

然后当我试图删除涉及这两行的某个对象时,sqlalchemy发现有约束被违反了,于是就抛出了'过时数据错误'(StaleDataError)。最后我只好手动从SQLite表里删除了一行重复的数据。

撰写回答