如何在SQLAlchemy中处理事务,包括认证用户等?
我正在使用SQLAlchemy的声明式扩展。我希望对所有表的变更进行记录,包括多对多关系的变更(映射表)。每个表应该有一个单独的“日志”表,结构类似,但需要额外的列来说明变更发生的时间、是谁做的变更等等。
我的编程模型大概是这样的:
row.foo = 1
row.log_version(username, change_description, ...)
理想情况下,系统不允许在没有调用row.log_version的情况下提交事务。
大家有什么想法吗?
2 个回答
我们在这个链接上有一个相当全面的“版本控制”教程:http://www.sqlalchemy.org/trac/wiki/UsageRecipes/LogVersions。看起来其他用户也对这个教程做了一些改进和变种。关于“在ORM层面发生变化时添加一行”的具体操作都在这里了。
另外,你也可以通过使用 ConnectionProxy
在执行层面进行拦截,想了解怎么用的话可以去SQLA的文档里查找相关信息。
补充一下:版本控制现在已经成为SQLA的一个示例,具体可以查看这里:http://docs.sqlalchemy.org/en/rel_0_8/orm/examples.html#versioned-objects
这里面有太多问题了,所以不能在一个回答里详细解答所有问题。如果你觉得我的简短提示不够清楚,可以单独提问。
给事务分配用户和描述
最常见的方法是把用户(和其他信息)分配给一个全局对象(在多线程应用中用的是threading.local()
)。这种方法不好,容易导致难以发现的错误。
更好的方法是把用户分配给会话。这种方法在每次网页请求创建会话时是可以的(其实,这也是有身份验证的应用的最佳设计),因为这个会话只有一个用户在使用。但通过这种方式传递描述就不太好了。
我最喜欢的解决方案是扩展Session.commit()
方法,让它可以接受一个可选的用户(可能还有其他信息)参数,并把它分配给当前事务。这种方法最灵活,也很适合传递描述。需要注意的是,这些信息是绑定到单个事务的,并且在事务关闭时以明显的方式传递。
发现变化
可以使用sqlalchemy.org.attributes.instance_state(obj)
来获取你需要的所有信息。对你来说最有用的可能是state.committed_state
字典,它包含了被更改字段的原始状态(包括多对多关系!)。还有state.get_history()
方法(或者sqlalchemy.org.attributes.get_history()
函数),它返回一个历史对象,里面有has_changes()
方法和added
、deleted
属性,分别表示新值和旧值。在后者的情况下,可以使用state.manager.keys()
(或者state.manager.attributes
)来获取所有字段的列表。
自动存储变化
SQLAlchemy支持映射器扩展,可以在更新、插入和删除之前和之后提供钩子。你需要提供自己的扩展,包含所有的前钩子(因为在刷新时对象的状态已经改变,所以不能使用后钩子)。对于声明式扩展,写一个DeclarativeMeta
的子类来为所有模型添加映射器扩展是很简单的。需要注意的是,如果你使用映射对象来记录日志,必须刷新两次更改,因为工作单元不计算在钩子中创建的对象。