基本上就是这么做的,没有现成的捷径.
当然,你可以概括如下:
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, True
2020年更新(仅限Python 3.9+)
这是一个使用Python 3.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(**kwargs)
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()
或可序列化事务模式,否则对于争用条件,这仍然不是one_or_none()
%的保证.