繁体   English   中英

与peewee一起使用事务而不使用`atomic()`

[英]Using transactions with peewee without using `atomic()`

我们有一个文件db.py ,其中定义了peewee数据库:

db = PostgresqlExtDatabase('mom',
                           user=DB_CONFIG['username'],
                           password=DB_CONFIG['password'],
                           host=DB_CONFIG['host'],
                           port=DB_CONFIG['port'],
                           threadlocals=True,
                           register_hstore=False,
                           autocommit=True,
                           autorollback=True,
                           cursor_factory=DictCursor)

调用db.execute("SOME RAW SQL UPDATE QUERY")可以按预期工作。 但是在此之前调用begin并不能阻止数据库被修改。

db.begin()
db.execute("SOME RAW SQL UPDATE QUERY")  # <- Does not wait, db is updated immediately here
db.commit()

我说的对吗?

我基本上需要将原始sql嵌套在一个事务中(如果已经在进行中),否则,如果没有事务begin调用就立即执行它。

如果我执行db.set_autocommit(False)然后execute_sql然后commit()这将按预期工作。
它也可以在atomic()上下文管理器内工作。


为了提供一些背景信息,我正在研究Web应用程序和物流,并且我们的代码库使用Flask和SQLAlchemy scoped_session并且autocommit设置为True。 它不使用SQLAlchemy ORM(由于历史原因),而仅使用Session对象及其execute()begin()begin_nested()rollback()remove()方法。

它的操作方式是在文件中定义一个Session Session = scoped_session(sessionmaker(autocommit=True)) ,然后在代码库中的任何地方调用session = Session() ,然后使用session.execute("SQL")执行查询session.execute("SQL")有时会调用session.begin() ,因此直到提交(或回滚)后查询才会执行。

现在,我们真的很想使用peewee。 但是..代码库是建立在此会话上的。 因此,这必须被欺骗。 不可能更改每个文件,并且没有足够的测试用例来启动(出于历史原因)。


我也有一些问题,但是我不知道在哪里问他们,所以我希望你不要介意我是否将它们放在这里:

  • 这个数据库对象(及其连接)是否绑定到正在执行的线程? 如果从两个不同的文件中导入db,并且从每个文件中分别调用db.begin() ,那么基本上会出现一些错误吗? 我可以在ipython shell中看到每个线程的上述db对象的id是相同的,因此我是否可以假设除非重新创建了psycopg2连接,否则应该将其隔离吗?

  • 为了欺骗sqlalchemy Session ,我创建了一个包装器类,该类返回所需的会话类型,SQLA Session对象或我为peewee编写的包装器来欺骗它。

     class SessionMocker(object): # DO NOT make this a singleton. Sessions will break def __init__(self, orm_type=ORM_TYPES.SQLA): assert orm_type in ORM_TYPES, "Invalid session constructor type" super(SessionMocker, self).__init__() self.orm_type = orm_type def __call__(self, *args, **kwargs): if self.orm_type == ORM_TYPES.SQLA: return SQLASession(*args, **kwargs) if self.orm_type == ORM_TYPES.PEEWEE: # For now lets assume no slave return SessionWrapper(*args, **kwargs) raise NotImplementedError def __getattr__(self, item): """ Assuming this will never be called without calling Session() first. Else there is no way to tell what type of Session class (ORM) is required, since that can't be passed. """ if self.orm_type == ORM_TYPES.SQLA: kls = SQLASession elif self.orm_type == ORM_TYPES.PEEWEE: kls = SessionWrapper else: raise NotImplementedError return getattr(kls, item) Session = SessionMocker(ORM_TYPES.SQLA) 

我认为这将使代码库可以透明无缝地切换到使用peewee,而不必在任何地方进行更改。 我怎样才能更好地做到这一点?

该文档说明了如何执行此操作: http : //docs.peewee-orm.com/en/latest/peewee/transactions.html#autocommit-mode

但是,tl; dr,您需要先禁用自动提交,然后开始/提交/回滚才能按预期工作:

db.set_autocommit(False)
db.begin()
try:
    user.delete_instance(recursive=True)
except:
    db.rollback()
    raise
else:
    try:
        db.commit()
    except:
        db.rollback()
        raise
finally:
    db.set_autocommit(True)

自动提交的默认值为True,但自动回滚的默认值为False。 将自动回滚设置为True会在执行查询时发生异常时自动回滚。 不能确定,但​​是这可能会弄乱情况。 因此,如果您想尝试将自动回滚设置为False

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM