小编典典

SQLAlchemy 是否与 Django 的 get_or_create 等效?

all

如果它已经存在(基于提供的参数),我想从数据库中获取一个对象,或者如果它不存在则创建它。

Django
get_or_create(或源代码)就是这样做的。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

阅读 106

收藏
2022-07-08

共1个答案

小编典典

这基本上就是这样做的方法,没有现成的捷径 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

2020 年更新(仅限 Python 3.9+)

这是使用 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% 保证不会出现竞争条件。

2022-07-08