【问题标题】:How to make sqlalchemy session keep track of all changes in the locally created objects?如何使 sqlalchemy 会话跟踪本地创建的对象中的所有更改?
【发布时间】:2013-09-09 02:16:23
【问题描述】:

如果我这样做,Sqlalchemy 会跟踪所有更改并提交到数据库:

b =  this_session.query(BaseUrl).filter_by(id=value).first()
b.basevalue = "new_value" #changing 
this_session.commit()     #commit the change to the DB

效果很好。

但是,如果我使用这个:

proxy =  this_session.execute("select * from base_url where id = {0}".format(value))
b = create_base_url(proxy) #create an instance of BaseUrl from proxy!
b.basevalue = "new_value"  #changing 
this_session.commit()      #it does NOT commit the change to the DB

当然,create_base_url() 的问题。在这个函数中,我只是通过传递从proxy 获取的各种参数并返回它来创建BaseUrl 的实例。为了让this_session 跟踪对象中的所有更改,我没有做任何其他事情。我需要这样的功能:

this_session.attach(b) #keep track of changes in b

以便跟踪b中的所有更改。

我应该如何实现这样的功能?如果我有相反的情况也很好:

this_session.detach(b) #don't keep track of changes in b

我正在使用postgresql 9.2 和sqlalchemy 0.9.0。

除了一些帮助之外,一个指向文档的链接——处理这个问题的部分——会很棒,所以我可以详细阅读它。 :-)

【问题讨论】:

    标签: python postgresql sqlalchemy commit


    【解决方案1】:

    您似乎需要阅读一些 SQLAlchemy 的原生文档。它写得很好,有助于解决很多问题,尤其是这些基本问题。使用 ORM 时有两个绝对必读:

    您必须在进入 SQLAlchemy 之前阅读这些内容。

    在我解决您的问题之前的下一件事:

    proxy =  this_session.execute("select * from base_url where id = {0}".format(value))
    

    永远,永远,这样做!这使您容易受到 SQL 注入攻击,并且您不希望这样,特别是如果您有这样一个强大的 ORM 为您处理它。有几种方法可以代替它。最简单的方法是使用为 Session.execute 记录的绑定参数:

    proxy =  this_session.execute("select * from base_url where id = :value", {"value": value})
    

    您也可以直接使用 SQLAlchemy 核心来创建漂亮的语句。为此,您可能想阅读同样出色的SQL Expression Language Tutorial。

    现在到您的实际问题:

    this_session.add(b)
    

    这就是你想要的attach:对象被添加到会话(和数据库)中。删除是双重的:如果你想从数据库中删除它:

    this_session.delete(b)
    

    如果您只想将其从会话中分离出来:

    this_session.expunge(b)
    

    但通常情况下,除非再次将它们添加到新会话中,否则不会将它们从会话中删除。对于正常的用例,这不是必须知道的,但也有很好的文档记录,因此很容易阅读。

    关于 SQL 注入(或为什么绑定参数很重要):

    我只是简单地说明问题,更广泛的解释请自行搜索,因为有很多资源。

    问题:假设您可以将value 设置为任何东西。您也可以将其设置为文字 SQL 语句,例如:

    value = "0 UNION SELECT * FROM admin_table"
    

    如果运行,它将产生:

    "select * from base_url where id = 0 UNION SELECT * FROM admin_table"
    

    我只是在这里举了一个例子,但想象一下这个表包含您的管理凭据。你能做的还有很多,但一切都很糟糕。但是现在让我们看看绑定参数是如何做到的:

    "select * from base_url where id = '0 UNION SELECT * FROM admin_table'"
    

    你看,它被引用了。是的,您也可以引用它,但这甚至会照顾到这一点(如果您感兴趣,请阅读,否则就这样做)。

    在 Session.add 上:

    你是对的,如果你创建一个已经存在的对象(你为什么要这样做?)然后 SQLAlchemy 将尝试添加它(因为它不知道你创建了现有的东西,它假设你会通过查询来检索它)。因此,如果这确实是您的用例,那么 Session.merge 可能会帮助您,或者您必须处理对象的状态(即使其“非瞬态”)。但在尝试这样做之前,请阅读上面链接的文档,因为我解释的所有内容(以及更多内容)都以更好的方式进行了解释。

    【讨论】:

    • 我不明白这两种 execute() 方法的区别?为什么我的很脆弱,而你的不是?你能详细说明一下吗?
    • 另外,this_session.add(b) 也尝试将b 添加到数据库中,但b 已经存在于数据库中。不是问题吗?
    • 如果value 是这样,那么{"value": value} 将变为{"value": "0 UNION SELECT * FROM admin_table"}。两者的问题是一样的,不是吗?
    • 请阅读该问题。它与实际问题无关,并且在使用谷歌时有很好的记录。搜索绑定参数和/或 SQL 注入。也可以尝试和尝试以获得更深入的了解。
    • 我自己找到了解决方案。请参阅我发布的答案。
    猜你喜欢
    • 2011-04-26
    • 2013-06-10
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 2012-01-02
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多