有两个类:用户和问题
用户可能有很多问题,并且还包含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()
一切顺利。
但我不想使用事件回调来做同样的事情。如下:
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()}
....
请注意“after_insert”方法:
instance.user # -> Get None!!!
为什么呢?
如果我将该行更改为:
Session.query(User).filter_by(id=instance.user_id).one()
我可以成功获得用户,但是:用户无法更新!
看,我修改了用户:
user.question_count += 1
但是在控制台中没有打印出'update'sql,而且
question_count
未更新。
我尝试添加Session.flush()
或Session.commit()
after_insert()
方法,但都会导致错误。
我有什么重要的事情要错过吗?请帮帮我,谢谢
答案 0 :(得分:7)
sqlalchemy的作者在论坛中给了我一个有用的答案,我在这里复制:
此外,一个关键的概念 工作单位模式就是它 组织所有的完整列表 INSERT,UPDATE和DELETE语句 将被发射,以及 它们的排放顺序, 事情发生之前。当。。。的时候 before_insert()和after_insert() 调用事件挂钩,这个结构 已经确定,不可能 以任何方式改变了。该 before_insert()和。的文档 before_update()提到了 冲洗计划不能受此影响 point - 仅限个别属性 手头的物体,以及那些物体 尚未插入或更新, 可以在这里受到影响。任何计划 想要改变同花顺 计划必须使用 SessionExtension.before_flush。 但是,有几种方法 在这里完成你想要的东西 没有修改冲洗计划。
最简单的就是我已经拥有的 建议。使用 MapperExtension.before_insert()上 “用户”类,并设置 user.question_count = LEN(user.questions)。这假定 你正在改变 user.questions集合,而不是 使用Question.user来 建立关系。如果你 碰巧正在使用“动态” 关系(事实并非如此) 在这里,你要拉历史 user.questions并计算什么 被追加并删除。
接下来的方法是做很多事 你认为你想要什么,就是这样 在问题上实现after_insert, 但是发出UPDATE语句 你自己。这就是“连接”的原因 映射器的一个参数 扩展方法:
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 你想避免许多特别的事情 UPDATE语句,实际上是 通过使用影响冲洗计划 SessionExtension.before_flush:
类 MySessionExtension(SessionExtension): def before_flush(self,session,flush_context): 对于session.new中的obj: if isinstance(obj,Question): obj.user.question_count + = 1
for obj in session.deleted: if isinstance(obj, Question): obj.user.question_count -= 1
结合“聚合”方法 用“before_flush”方法 “自己发出SQL”的方法 你可以使用after_insert()方法 也使用SessionExtension.after_flush, 计算一切并发出一个 单个质量UPDATE语句有很多 参数。我们可能很好 这个特殊的过度杀戮的领域 情况,但我举了一个例子 去年在Pycon的这样一个计划, 你可以看到 http://bitbucket.org/zzzeek/pycon2010/src/tip/chap5/sessionextension.py
而且,正如我尝试的那样,我发现我们应该更新user.question_count
中的after_flush
答案 1 :(得分:2)
user
,假设一个RelationshipProperty,只在刷新后填充(因为只有这一点,ORM知道如何关联这两行)。
看起来question_count
实际上是派生属性,是该用户的Question行数。如果不考虑性能,则可以使用只读属性并让映射器完成工作:
@property
def question_count(self):
return len(self.questions)
否则你正在寻找在数据库级或python中实现触发器(它修改了刷新计划,因此更复杂)。