如果对象已经存在(基于提供的参数),我希望从数据库中获取该对象,如果不存在,则创建该对象。
Django的get_or_create (或source)可以做到这一点。在SQLAlchemy中有没有类似的快捷方式?
我现在明确地写出来,就像这样:
def get_or_create_instrument(session, serial_number):
instrument = session.query(Instrument).filter_by(serial_number=serial_number).first()
if instrument:
return instrument
else:
instrument = Instrument(serial_number)
session.add(instrument)
return instrument发布于 2010-04-07 01:47:07
这基本上就是做这件事的方式,AFAIK没有现成的捷径。
当然,您可以对其进行泛化:
def get_or_create(session, model, defaults=None, **kwargs):
instance = session.query(model).filter_by(**kwargs).one_or_none()
if instance:
return instance, False
else:
params = {k: v for k, v in kwargs.items() if not isinstance(v, ClauseElement)}
params.update(defaults or {})
instance = model(**params)
try:
session.add(instance)
session.commit()
except Exception: # The actual exception depends on the specific database so we catch all exceptions. This is similar to the official documentation: https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html
session.rollback()
instance = session.query(model).filter_by(**kwargs).one()
return instance, False
else:
return instance, True2020更新(仅限Python 3.9+ )
这是一个带有Python3.9的the new dict union operator (|=)的更干净的版本
def get_or_create(session, model, defaults=None, **kwargs):
instance = session.query(Model).filter_by(**kwargs).one_or_none()
if instance:
return instance, False
else:
kwargs |= defaults or {}
instance = model(**params)
try:
session.add(instance)
session.commit()
except Exception: # The actual exception depends on the specific database so we catch all exceptions. This is similar to the official documentation: https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html
session.rollback()
instance = session.query(model).filter_by(**kwargs).one()
return instance, False
else:
return instance, True注意:
类似于Django版本,这将捕获重复的键约束和类似的错误。如果您的get或create不能保证返回单个结果,它仍然可能导致争用条件。
为了缓解这个问题,您需要在session.commit()之后添加另一个one_or_none()样式的fetch。除非您还使用with_for_update()或可序列化事务模式,否则对于竞争条件,这仍然不是100%的保证。
发布于 2011-05-21 06:08:42
遵循@WoLpH的解决方案,这是对我有效的代码(简单版本):
def get_or_create(session, model, **kwargs):
instance = session.query(model).filter_by(**kwargs).first()
if instance:
return instance
else:
instance = model(**kwargs)
session.add(instance)
session.commit()
return instance有了这个,我就能够get_or_create我的模型中的任何对象了。
假设我的模型对象是:
class Country(Base):
__tablename__ = 'countries'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)为了获取或创建我的对象,我写道:
myCountry = get_or_create(session, Country, name=countryName)发布于 2014-01-16 03:30:24
我一直在尝试解决这个问题,最终得到了一个相当健壮的解决方案:
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我只是写了一个关于所有细节的fairly expansive blog post,但是有一些关于我为什么使用这个的相当多的想法。
@classmethod修饰的创建者函数(以及特定于这些函数的属性)。编辑:我已经按照this blog post中的解释将session.commit()更改为session.flush()。请注意,这些决策特定于所使用的数据存储(在本例中为Postgres)。
编辑2:我已经在函数中使用{}作为默认值进行了更新,因为这是典型的Python陷阱。谢谢你的the comment,奈杰尔!如果你对这个问题很好奇,可以看看this StackOverflow question和this blog post。
https://stackoverflow.com/questions/2546207
复制相似问题