sqlalchemy flush()并获取ID?


114

我想做这样的事情:

f = Foo(bar='x')
session.add(f)
session.flush()

# do additional queries using f.id before commit()
print f.id # should be not None

session.commit()

但是f.idNone我尝试的时候。我怎样才能使它工作?


2
您可以使用初始化SA引擎echo=True,并查看在刷新时执行什么SQL吗?您描述的内容应该可以正常工作并提供ID,但是可能还有其他一些问题导致f.id为None。
帕维尔·列宾

Answers:


63

您的示例代码应该已经按原样工作。SQLAlchemy应该为其提供一个值f.id,并假设其为自动生成的主键列。主键属性在flush()生成时立即在过程中填充,并且不需要调用commit()。因此,这里的答案在于以下一项或多项:

  1. 映射的详细信息
  2. 如果使用的后端有任何奇怪现象(例如,SQLite不会为复合主键生成整数值)
  3. 打开echo时发出的SQL表示什么

1
没错,在外壳中进行快速检查后会显示它使用值填充了主键字段。我将不得不调查为什么它在实践中不起作用。
Eloff

3
“您的示例代码应该提供一个值”听起来像是在说“您应该首先为id赋予一个值”,而不是“您应该拥有的代码按原样工作”。对我来说很清楚,只有在三读后,您才是指后者而不是前者。你能澄清一下吗?
随机出现

126

我遇到了同样的问题,经过测试,我发现这些答案中没有一个是足够的。

当前,或者从sqlalchemy .6+开始,有一个非常简单的解决方案(我不知道它是否存在于以前的版本中,尽管我认为确实存在):

session.refresh()

因此,您的代码将如下所示:

f = Foo(bar=x)
session.add(f)
session.flush()
# At this point, the object f has been pushed to the DB, 
# and has been automatically assigned a unique primary key id

f.id
# is None

session.refresh(f)
# refresh updates given object in the session with its state in the DB
# (and can also only refresh certain attributes - search for documentation)

f.id
# is the automatically assigned primary key ID given in the database.

就是这样。


8
这接近一个可能对我有用的答案,但我收到以下错误:InvalidRequestError:无法刷新实例“ <....>”。刷新后,该实例似乎不再存在。赞赏任何见识。
PlaidFan 2011年

1
你刚刚救了我的屁股。我认为我不会再使用来自Django的ORM。flush()命令不能按IMHO所述工作。
马克

2
更新,我不得不使用sessionmaker(autoflush=True),w / refresh()组合为我提供了行ID。#grrr
Marc

5
如果你不明白之间的差别flush()commit(),这里有一个很好的解释:stackoverflow.com/a/4202016/1252290
太平洋业务中心

2
@PlaidFan而不是flush()使用commit()它,而是在此之后-用刷新它session.refresh(f),对我有用,我使用SQLAlchemy版本0.6.7
Ricky Levi

20

谢谢大家 我通过修改列映射解决了我的问题。对我来说,autoincrement=True是必需的。

起源:

id = Column('ID', Integer, primary_key=True, nullable=False)

修改后:

id = Column('ID', Integer, primary_key=True, autoincrement=True, nullable=True)

然后

session.flush()  
print(f.id)

没关系!


6

与dpb给出的答案不同,不需要刷新。刷新后,您可以访问id字段,sqlalchemy会自动刷新在后端自动生成的id

我遇到了这个问题,经过一番调查后找出了确切的原因,我的模型是用id作为integerfield创建的,而在我的表单中,id用hiddenfield表示(因为我不想在表单中显示id)。默认情况下,隐藏字段表示为文本。一旦我使用widget = hiddenInput()将表单更改为integerfield,问题就解决了。


1
如前所述,只有refresh()对我有用。在进行数据迁移时,我需要循环使用行ID来填充FK。我尝试过提交,刷新,会话hacking和refresh()的每个组合都是唯一有效的方法。我的数据非常干净,我只是发现SQLA并不是那么好(至少在5个主要ORMS中具有相当的经验)。仅仅花了5个多小时,试图获取add()-> commit()/ flush()的行ID。
马克

1

我曾经0在调用session.add方法之前分配给ID时遇到问题。该ID已由数据库正确分配,但在之后的会话中未检索到正确的ID session.flush()


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.