【问题标题】:Why does a query invoke a auto-flush in SQLAlchemy?为什么查询会在 SQLAlchemy 中调用自动刷新?
【发布时间】:2015-12-31 13:14:39
【问题描述】:

您在上面看到的代码只是一个示例,但它可以重现此错误:

sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; 
consider using a session.no_autoflush block if this flush is occurring prematurely)
(sqlite3.IntegrityError) NOT NULL constraint failed: X.nn 
[SQL: 'INSERT INTO "X" (nn, val) VALUES (?, ?)'] [parameters: (None, 1)]

映射的实例仍会添加到会话中。该实例想知道(这意味着对数据库的查询)是否存在其自身类型的其他实例具有相同的值。还有第二个属性/列 (_nn)。它被指定为NOT NULL。但默认是NULL

当实例(如示例中的)仍添加到会话中时,调用 query.one() 会调用自动刷新。此刷新创建一个尝试存储实例的INSERT。这会失败,因为 _nn 仍然为 null 并且违反了 NOT NULL 约束。

这是我目前所理解的。 但问题是为什么它会调用自动刷新?我可以阻止它吗?

#!/usr/bin/env python3

import os.path
import os
import sqlalchemy as sa 
import sqlalchemy.orm as sao
import sqlalchemy.ext.declarative as sad
from sqlalchemy_utils import create_database

_Base = sad.declarative_base()
session = None


class X(_Base):
    __tablename__ = 'X'

    _oid = sa.Column('oid', sa.Integer, primary_key=True)
    _nn = sa.Column('nn', sa.Integer, nullable=False) # NOT NULL!
    _val = sa.Column('val', sa.Integer)

    def __init__(self, val):
        self._val = val

    def test(self, session):
        q = session.query(X).filter(X._val == self._val)
        x = q.one()
        print('x={}'.format(x))

dbfile = 'x.db'

def _create_database():
    if os.path.exists(dbfile):
        os.remove(dbfile)

    engine = sa.create_engine('sqlite:///{}'.format(dbfile), echo=True)
    create_database(engine.url)
    _Base.metadata.create_all(engine)
    return sao.sessionmaker(bind=engine)()


if __name__ == '__main__':
    session = _create_database()

    for val in range(3):
        x = X(val)
        x._nn = 0
        session.add(x)
    session.commit()

    x = X(1)
    session.add(x)
    x.test(session)

当然,解决方案是在调用query.one() 之前将实例添加到会话中。这项工作。但在我的真实(但对于这个问题来说很复杂)用例中,这不是一个好的解决方案。

【问题讨论】:

    标签: python sql python-3.x sqlalchemy


    【解决方案1】:

    如何关闭autoflush功能:

    • 临时:您可以在查询数据库的 sn-p 上使用no_autoflush 上下文管理器,即在X.test 方法中:

      def test(self, session):
          with session.no_autoflush:
              q = session.query(X).filter(X._val == self._val)
              x = q.one()
              print('x={}'.format(x))
      
    • 会话范围:只需将 autoflush=False 传递给您的会话创建者:

      return sao.sessionmaker(bind=engine, autoflush=False)()
      

    【讨论】:

    • 有人能解释一下为什么需要这样做吗?
    • @JonathanLeaders 在答案中有一个指向解释自动刷新功能的文档的链接。如果还有不清楚的地方,您最好提出具体问题。
    • 原来我没有在应该打电话的时候打电话给db.commit(),所以这让我觉得autoflush没有按预期工作,所以我的问题是基于一个错误。
    • @JonathanLeaders:引用上面链接的文档的no_autoflush 部分:“这在初始化一系列涉及现有数据库查询的对象时很有用,其中未完成的对象不应该被刷新。 "
    【解决方案2】:

    我知道这是旧的,但它可能对其他一些在使用 flask-sqlalchemy 时遇到此错误的人有所帮助。下面的代码解决了我的自动刷新问题。

    db = SQLAlchemy(session_options={"autoflush": False})
    

    【讨论】:

      猜你喜欢
      • 2017-04-03
      • 1970-01-01
      • 1970-01-01
      • 2023-01-25
      • 2010-10-31
      • 1970-01-01
      • 1970-01-01
      • 2012-12-23
      • 2011-07-10
      相关资源
      最近更新 更多