<p>我一直在处理这个问题,最后得到了一个相当可靠的解决方案:</p>
<pre class="lang-py prettyprint-override"><code>def get_one_or_create(session,
model,
create_method='',
create_method_kwargs=None,
**kwargs):
try:
return session.query(model).filter_by(**kwargs).one(), False
except NoResultFound:
kwargs.update(create_method_kwargs or {})
created = getattr(model, create_method, model)(**kwargs)
try:
session.add(created)
session.flush()
return created, True
except IntegrityError:
session.rollback()
return session.query(model).filter_by(**kwargs).one(), False
</code></pre>
<p>我只是在所有细节上写了一个<a href="http://skien.cc/blog/2014/01/15/sqlalchemy-and-race-conditions-implementing/" rel="noreferrer">fairly expansive blog post</a>,但是有一些关于我为什么使用这个的想法。</p>
<ol>
<li><p>它解包成一个元组,告诉您对象是否存在。这在您的工作流程中通常很有用。</p></li>
<li><p>该函数提供了使用<code>@classmethod</code>修饰的creator函数(以及特定的属性)的能力。</p></li>
<li><p>当有多个进程连接到数据存储时,该解决方案可防止竞争条件。</p></li>
</ol>
<p>编辑:我已将<code>session.commit()</code>更改为<code>session.flush()</code>,如<a href="http://skien.cc/blog/2014/02/06/sqlalchemy-and-race-conditions-follow-up/" rel="noreferrer">this blog post</a>中所述。注意,这些决定是特定于所使用的数据存储的(在本例中是Postgres)。</p>
<p>编辑2:我在函数中使用{}作为默认值进行了更新,因为这是典型的Python gotcha。谢谢你<a href="http://skien.cc/blog/2014/02/06/sqlalchemy-and-race-conditions-follow-up/#comment-1371570667" rel="noreferrer">the comment</a>,奈杰尔!如果你对此感到好奇,请查看<a href="https://stackoverflow.com/questions/5712904/empty-dictionary-as-default-value-for-keyword-argument-in-python-function-dicti">this StackOverflow question</a>和<a href="http://pythonconquerstheuniverse.wordpress.com/category/python-gotchas/" rel="noreferrer">this blog post</a>。</p>