如果它已经存在(基于提供的参数),我想从数据库中获取一个对象,或者如果它不存在则创建它。
Django 的get_or_create(或源代码)就是这样做的。SQLAlchemy 中是否有等效的快捷方式?
get_or_create
我目前正在像这样明确地写出来:
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
这基本上就是这样做的方法,没有现成的捷径 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, True
这是使用 Python 3.9 的新字典联合运算符 (|=)的更简洁版本
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 不能保证返回单个结果,它仍然可能导致竞争条件。
为了缓解其中的一些问题,您需要one_or_none()在session.commit(). with_for_update()除非您还使用或可序列化事务模式,否则这仍然不能 100% 保证不会出现竞争条件。
one_or_none()
session.commit()
with_for_update()