sqlalchemy的MapperExtension的一些问题 [英] Some problems with MapperExtension of sqlalchemy

查看:200
本文介绍了sqlalchemy的MapperExtension的一些问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有两类:用户和问题

用户可能有很多问题,并且还包含一个question_count 记录属于他的问题的数量.

A user may have many questions, and it also contains a question_count to record the the count of questions belong to him.

因此,当我添加一个新问题时,我想更新该问题的 用户.首先,我是这样的:

So, when I add a new question, I want update the question_count of the user. At first, I do as:

question = Question(title='aaa', content='bbb') 
Session.add(question) 
Session.flush() 


user = question.user 
### user is not None 
user.question_count += 1 
Session.commit() 

一切顺利.

但是我不会使用事件回调来做同样的事情.如下:

But I wan't to use event callback to do the same thing. As following:

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"方法中的注释:

  1. Note in the "after_insert" method:

instance.user # -> Get None!!!

为什么?

如果我将该行更改为:

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

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

I can get the user successfully, But: the user can't be updated!

我已经修改了用户:

user.question_count += 1

但是控制台中没有打印'update'sql,并且 question_count未更新.

But there is no 'update' sql printed in the console, and the question_count are not updated.

我尝试在

  • 中添加Session.flush()Session.commit() after_insert()方法,但是两者都会导致错误.

    I try to add Session.flush() or Session.commit() in the after_insert() method, but both cause errors.

    我有什么重要的东西想念吗?请帮助我,谢谢

    Is there any important thing I'm missing? Please help me, thank you

    推荐答案

    sqlalchemy的作者在一个论坛上给了我一个有用的答案,我将其复制到这里:

    The author of sqlalchemy gave me an useful answer in a forum, I copy it here:

    此外, 工作单元模式是 整理所有的完整列表 INSERT,UPDATE和DELETE语句 将被发射,以及 发出它们的顺序, 在任何事情发生之前.当...的时候 before_insert()和after_insert() 事件挂钩被称为这种结构 已经确定,不能 以任何方式改变.这 before_insert()和 before_update()提到 冲水计划在此不会受到影响 点-仅在上的单个属性 手边的物体,以及那些 尚未插入或更新, 在这里可能会受到影响.任何计划 想要改变同花顺 计划必须使用 SessionExtension.before_flush. 但是,有几种方法可以 在这里完成你想要的 无需修改冲水计划.

    Additionally, a key concept of the unit of work pattern is that it organizes a full list of all INSERT,UPDATE, and DELETE statements which will be emitted, as well as the order in which they are emitted, before anything happens. When the before_insert() and after_insert() event hooks are called, this structure has been determined, and cannot be changed in any way. The documentation for before_insert() and before_update() mentions that the flush plan cannot be affected at this point - only individual attributes on the object at hand, and those which have not been inserted or updated yet, can be affected here. Any scheme which would like to change the flush plan must use SessionExtension.before_flush. However, there are several ways of accomplishing what you want here without modifiying the flush plan.

    最简单的就是我已经 建议.使用 MapperExtension.before_insert() 用户"类,并设置 user.question_count = len(user.questions).这是假设 你正在变异 user.questions集合,而不是 与Question.user合作 建立关系.如果你 碰巧正在使用动态" 关系(事实并非如此) 在这里),您可以提取历史记录 user.questions并计算出什么 被附加和删除.

    The simplest is what I already suggested. Use MapperExtension.before_insert() on the "User" class, and set user.question_count = len(user.questions). This assumes that you are mutating the user.questions collection, rather than working with Question.user to establish the relationship. If you happened to be using a "dynamic" relationship (which is not the case here), you'd pull the history for user.questions and count up what's been appended and removed.

    下一步是做很多事情 你以为你想要的就是 对问题实施after_insert, 但发出UPDATE语句 你自己.这就是为什么连接"是 映射器的参数之一 扩展方法:

    The next way, is to do pretty much what you think you want here, that is implement after_insert on Question, but emit the UPDATE statement yourself. That's why "connection" is one of the arguments to the mapper extension methods:

    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:

    I wouldn't prefer that approach since it's quite wasteful for many new Questions being added to a single User. So yet another option, if User.questions cannot be relied upon and you'd like to avoid many ad-hoc UPDATE statements, is to actually affect the flush plan by using SessionExtension.before_flush:

    班 MySessionExtension(SessionExtension): def before_flush(self,session,flush_context): 对于session.new中的obj: 如果isinstance(obj,Question): obj.user.question_count + = 1

    class MySessionExtension(SessionExtension): def before_flush(self, session, flush_context): for obj in session.new: 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

    To combine the "aggregate" approach of the "before_flush" method with the "emit the SQL yourself" approach of the after_insert() method, you can also use SessionExtension.after_flush, to count everything up and emit a single mass UPDATE statement with many parameters. We're likely well in the realm of overkill for this particular situation, but I presented an example of such a scheme at Pycon last year, which you can see at http://bitbucket.org/zzzeek/pycon2010/src/tip/chap5/sessionextension.py .

    而且,正如我尝试的那样,我发现我们应该更新after_flush中的user.question_count

    And, as I tried, I found we should update the user.question_count in after_flush

    这篇关于sqlalchemy的MapperExtension的一些问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆