Peewee 提供了几个用于处理事务的接口。最通用的是Database.atomic()方法,它也支持嵌套事务。atomic()块将在事务或保存点中运行,具体取决于嵌套级别。
如果包装块中发生异常,则当前事务/保存点将回滚。否则,语句将在包装块的末尾提交。
笔记
在由上下文管理器包装的块内时,您可以通过调用或atomic()在任何时候显式回滚或提交
。当您在包装的代码块中执行此操作时,将自动启动一个新事务。Transaction.rollback()Transaction.commit()
with db.atomic() as transaction: # Opens new transaction.
try:
save_some_objects()
except ErrorSavingData:
# Because this block of code is wrapped with "atomic", a
# new transaction will begin automatically after the call
# to rollback().
transaction.rollback()
error_saving = True
create_report(error_saving=error_saving)
# Note: no need to call commit. Since this marks the end of the
# wrapped block of code, the `atomic` context manager will
# automatically call commit for us.
笔记
atomic()可以用作上下文管理器或装饰器。
atomic用作上下文管理器:
db = SqliteDatabase(':memory:')
with db.atomic() as txn:
# This is the outer-most level, so this block corresponds to
# a transaction.
User.create(username='charlie')
with db.atomic() as nested_txn:
# This block corresponds to a savepoint.
User.create(username='huey')
# This will roll back the above create() query.
nested_txn.rollback()
User.create(username='mickey')
# When the block ends, the transaction is committed (assuming no error
# occurs). At that point there will be two users, "charlie" and "mickey".
您也可以使用该atomic方法执行get 或 create操作:
try:
with db.atomic():
user = User.create(username=username)
return 'Success'
except peewee.IntegrityError:
return 'Failure: %s is already in use.' % username
atomic用作装饰器:
@db.atomic()
def create_user(username):
# This statement will run in a transaction. If the caller is already
# running in an `atomic` block, then a savepoint will be used instead.
return User.create(username=username)
create_user('charlie')
atomic()提供透明的事务嵌套。使用时atomic(),最外层的调用将被包装在事务中,任何嵌套调用都将使用保存点。
with db.atomic() as txn:
perform_operation()
with db.atomic() as nested_txn:
perform_another_operation()
Peewee 通过使用保存点支持嵌套事务(有关更多信息,请参阅 参考资料savepoint())。
如果您希望在事务中显式运行代码,可以使用 transaction(). 像atomic(), transaction()可以用作上下文管理器或装饰器。
如果包装块中发生异常,则事务将回滚。否则,语句将在包装块的末尾提交。
db = SqliteDatabase(':memory:')
with db.transaction() as txn:
# Delete the user and their associated tweets.
user.delete_instance(recursive=True)
事务可以在包装块内显式提交或回滚。发生这种情况时,将启动一个新事务。
with db.transaction() as txn:
User.create(username='mickey')
txn.commit() # Changes are saved and a new transaction begins.
User.create(username='huey')
# Roll back. "huey" will not be saved, but since "mickey" was already
# committed, that row will remain in the database.
txn.rollback()
with db.transaction() as txn:
User.create(username='whiskers')
# Roll back changes, which removes "whiskers".
txn.rollback()
# Create a new row for "mr. whiskers" which will be implicitly committed
# at the end of the `with` block.
User.create(username='mr. whiskers')
笔记
如果您尝试使用 transaction()上下文管理器将事务与 peewee
嵌套,则只会使用最外层的事务。但是,如果嵌套块中发生异常,这可能会导致不可预知的行为,因此强烈建议您使用atomic().
正如您可以显式创建事务一样,您也可以使用该savepoint()方法显式创建保存点。保存点必须出现在事务中,但可以嵌套任意深度。
with db.transaction() as txn:
with db.savepoint() as sp:
User.create(username='mickey')
with db.savepoint() as sp2:
User.create(username='zaizee')
sp2.rollback() # "zaizee" will not be saved, but "mickey" will be.
警告
如果您手动提交或回滚保存点,则不会自动创建新的保存点。这与 的行为不同 transaction,后者会在手动提交/回滚后自动打开一个新事务。
默认情况下,Peewee 在自动提交模式下运行,这样在事务之外执行的任何语句都在它们自己的事务中运行。为了将多个语句组合成一个事务,Peewee 提供了 atomic()上下文管理器/装饰器。这应该涵盖所有用例,但万一您想完全暂时禁用 Peewee 的事务管理,您可以使用 Database.manual_commit()上下文管理器/装饰器。
以下是您可以如何模拟 transaction()上下文管理器的行为:
with db.manual_commit():
db.begin() # Have to begin transaction explicitly.
try:
user.delete_instance(recursive=True)
except:
db.rollback() # Rollback! An error occurred.
raise
else:
try:
db.commit() # Commit changes.
except:
db.rollback()
raise
再说一次——我预计没有人需要这个,但它在这里以防万一。
Python DB-API 2.0 规范描述了几种类型的异常。因为大多数数据库驱动程序都有自己的这些异常的实现,Peewee 通过为任何特定于实现的异常类提供自己的包装器来简化事情。这样,您不必担心导入任何特殊的异常类,您可以使用 peewee 中的那些:
笔记
所有这些错误类都扩展了PeeweeException.
所有查询都使用标准库 模块记录到peewee命名空间。logging使用DEBUG级别记录查询。如果您有兴趣对查询做一些事情,您可以简单地注册一个处理程序。
# Print all queries to stderr.
import logging
logger = logging.getLogger('peewee')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
Peewee 内置了对 Postgres、MySQL 和 SQLite 的支持。这些数据库非常流行,从快速、可嵌入的数据库到适合大规模部署的重量级服务器,应有尽有。话虽这么说,有很多很酷的数据库,如果驱动程序支持 DB-API 2.0 规范,添加对您选择的数据库的支持应该非常容易。
如果您使用过标准库 sqlite3 驱动程序、psycopg2 等,那么您应该熟悉 DB-API 2.0 规范。Peewee 目前依赖于几个部分:
第一件事是提供一个Database将打开连接的子类。
from peewee import Database
import foodb # Our fictional DB-API 2.0 driver.
class FooDatabase(Database):
def _connect(self, database, **kwargs):
return foodb.connect(database, **kwargs)
提供更高级别的DatabaseAPI 并负责执行查询、创建表和索引以及自省数据库以获取表列表。上述实现是绝对最低要求,尽管某些功能将不起作用——为了获得最佳结果,您将需要另外添加一个方法,用于从数据库中提取表列表和表的索引。我们假设它FooDB很像 MySQL 并且有特殊的“SHOW”语句:
class FooDatabase(Database):
def _connect(self):
return foodb.connect(self.database, **self.connect_params)
def get_tables(self):
res = self.execute('SHOW TABLES;')
return [r[0] for r in res.fetchall()]
此处未涉及的数据库处理的其他内容包括:
笔记
如果您的驱动程序符合 DB-API 2.0 规范,那么启动和运行应该不需要太多工作。
我们的新数据库可以像任何其他数据库子类一样使用:
from peewee import *
from foodb_ext import FooDatabase
db = FooDatabase('my_database', user='foo', password='secret')
class BaseModel(Model):
class Meta:
database = db
class Blog(BaseModel):
title = CharField()
contents = TextField()
pub_date = DateTimeField()