sqlalchemy的MapperExtension的一些问题

时间:2010-09-05 15:00:55

标签: insert sqlalchemy

有两个类:用户和问题

用户可能有很多问题,并且还包含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()} 
    .... 
  1. 请注意“after_insert”方法:

    instance.user # -> Get None!!!

    为什么呢?

  2. 如果我将该行更改为:

    Session.query(User).filter_by(id=instance.user_id).one()

    我可以成功获得用户,但是:用户无法更新!

    看,我修改了用户:

    user.question_count += 1

    但是在控制台中没有打印出'update'sql,而且 question_count未更新。

  3. 我尝试添加Session.flush()Session.commit() after_insert()方法,但都会导致错误。

  4. 我有什么重要的事情要错过吗?请帮帮我,谢谢

2 个答案:

答案 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中实现触发器(它修改了刷新计划,因此更复杂)。