如何使sqlalchemy会话跟踪本地创建的对象中的所有更改?

Naw*_*waz 2 python postgresql sqlalchemy commit

如果我这样做,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
Run Code Online (Sandbox Code Playgroud)

这很好用.

但是,如果我使用这个:

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
Run Code Online (Sandbox Code Playgroud)

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

this_session.attach(b) #keep track of changes in b
Run Code Online (Sandbox Code Playgroud)

以便跟踪所有变化b.

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

this_session.detach(b) #don't keep track of changes in b
Run Code Online (Sandbox Code Playgroud)

我正在使用postgresql 9.2sqlalchemy 0.9.0.

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

jav*_*vex 5

看起来好像你需要阅读一些SQLAlchemy的原生文档.它编写得非常好,可以解决很多问题,尤其是基本问题.使用ORM时有两个绝对必须读取:

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

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

proxy =  this_session.execute("select * from base_url where id = {0}".format(value))
Run Code Online (Sandbox Code Playgroud)

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

proxy =  this_session.execute("select * from base_url where id = :value", {"value": value})
Run Code Online (Sandbox Code Playgroud)

您也可以直接使用SQLAlchemy核心来创建漂亮的语句.为此,您可能希望阅读优秀的SQL表达式语言教程.

现在你的实际问题:

this_session.add(b)
Run Code Online (Sandbox Code Playgroud)

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

this_session.delete(b)
Run Code Online (Sandbox Code Playgroud)

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

this_session.expunge(b)
Run Code Online (Sandbox Code Playgroud)

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

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

我只会简单地说明问题,为了更广泛的解释,请搜索自己,因为有足够的资源.

问题:想象一下,你可以设置value任何东西.您还可以将其设置为文字SQL语句,例如:

value = "0 UNION SELECT * FROM admin_table"
Run Code Online (Sandbox Code Playgroud)

如果运行它会产生:

"select * from base_url where id = 0 UNION SELECT * FROM admin_table"
Run Code Online (Sandbox Code Playgroud)

我在这里举了一个例子,但想象一下这个表包含你的管理凭据.你可以做更多的事情,这一切都很糟糕.但现在让我们看看绑定参数如何做到这一点:

"select * from base_url where id = '0 UNION SELECT * FROM admin_table'"
Run Code Online (Sandbox Code Playgroud)

你看,它被引用了.是的,你也可以引用它,但这甚至需要注意(如果你有兴趣,请阅读,否则就这样做).

在Session.add上:

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