有两个类:用户和问题
用户可能有很多问题,并且还包含一个question_count来记录属于他的问题的数量.
所以,当我添加一个新问题时,我想更新用户的question_count.起初,我这样做:
question = Question(title='aaa', content='bbb')
Session.add(question)
Session.flush()
user = question.user
### user is not None
user.question_count += 1
Session.commit()
Run Code Online (Sandbox Code Playgroud)
一切顺利.
但我不想使用事件回调来做同样的事情.如下:
from sqlalchemy.orm.interfaces import MapperExtension
class Callback(MapperExtension):
def after_insert(self, mapper, connection, instance):
user = instance.user
### user is None !!!
user.question_count += 1
class Question(Base):
__tablename__ = "questions"
__mapper_args__ = {'extension':Callback()}
....
Run Code Online (Sandbox Code Playgroud)
请注意"after_insert"方法:
instance.user # -> Get None!!!
为什么?
如果我将该行更改为:
Session.query(User).filter_by(id=instance.user_id).one()
我可以成功获得用户,但是:用户无法更新!
看我修改了用户:
user.question_count += 1
但是控制台question_count中没有打印"update"sql,并且
没有更新.
我尝试添加Session.flush()或Session.commit()在
after_insert()方法中,但都会导致错误.
有什么重要的事情我想念吗?请帮帮我,谢谢
sqlalchemy的作者在论坛中给了我一个有用的答案,我在这里复制:
此外,工作单元模式的一个关键概念是它在发生任何事情之前组织将发出的所有INSERT,UPDATE和DELETE语句的完整列表,以及它们的发出顺序.调用before_insert()和after_insert()事件挂钩时,此结构已确定,无法以任何方式更改.before_insert()和before_update()的文档提到此时刷新计划不会受到影响 - 只有手头对象上的单个属性以及尚未插入或更新的属性可能会受到影响.任何想要更改刷新计划的方案都必须使用SessionExtension.before_flush.然而,
最简单的是我已经提出的建议.在"User"类上使用MapperExtension.before_insert(),并设置user.question_count = len(user.questions).这假设您正在改变user.questions集合,而不是使用Question.user来建立关系.如果您碰巧使用"动态"关系(这里不是这种情况),您可以提取user.questions的历史记录并计算已追加和删除的内容.
接下来的方法是,在这里做几乎你想要的事情,就是在Question上实现after_insert,但是自己发出UPDATE语句.这就是为什么"连接"是映射器扩展方法的参数之一:
Run Code Online (Sandbox Code Playgroud)def after_insert(self, mapper, connection, instance): connection.execute(users_table.update().\ values(question_count=users_table.c.question_count +1).\ where(users_table.c.id==instance.user_id))我不喜欢这种方法,因为对于添加到单个用户的许多新问题来说,这是非常浪费的.所以另一种选择,如果不能依赖User.questions并且你想避免许多ad-hoc UPDATE语句,那么实际上是通过使用SessionExtension.before_flush来影响刷新计划:
class MySessionExtension(SessionExtension):def before_flush(self,session,flush_context):对于session.new中的obj:if isinstance(obj,Question):obj.user.question_count + = 1
Run Code Online (Sandbox Code Playgroud)for obj in session.deleted: if isinstance(obj, Question): obj.user.question_count -= 1要将"before_flush"方法的"聚合"方法与after_insert()方法的"自己发出SQL"方法相结合,您还可以使用SessionExtension.after_flush来计算所有内容并发出一个包含许多UPDATE语句的UPDATE语句.参数.对于这种特殊情况,我们很可能处于矫枉过正的境地,但去年我在Pycon上提出了这样一个计划的例子,你可以在http://bitbucket.org/zzzeek/pycon2010/src/tip/看到. chap5/sessionextension.py .
并且,正如我尝试的那样,我发现我们应该更新user.question_countinafter_flush
| 归档时间: |
|
| 查看次数: |
1285 次 |
| 最近记录: |