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

Django的get_or_create(或source)可以做到这一点.炼金术中有没有类似的捷径?

我现在明确地这样写:

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

推荐答案

基本上就是这么做的,没有现成的捷径.

当然,你可以概括如下:

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()%的保证.

Python相关问答推荐

如何使用函数正确索引收件箱?

如何计算部分聚合数据的统计数据

具有2D功能的Python十六进制图

将行从一个DF添加到另一个DF

不允许AMBIMA API请求方法

当密钥是复合且唯一时,Pandas合并抱怨标签不唯一

Pandas 第二小值有条件

TARete错误:类型对象任务没有属性模型'

根据不同列的值在收件箱中移动数据

如何比较numPy数组中的两个图像以获取它们不同的像素

Deliveryter Notebook -无法在for循环中更新matplotlib情节(保留之前的情节),也无法使用动画子功能对情节进行动画

删除最后一个pip安装的包

为什么sys.exit()不能与subproccess.run()或subprocess.call()一起使用

Pandas—合并数据帧,在公共列上保留非空值,在另一列上保留平均值

numpy卷积与有效

Pandas DataFrame中行之间的差异

如何保持服务器发送的事件连接活动?

如何更改groupby作用域以找到满足掩码条件的第一个值?

如何使用OpenGL使球体遵循Python中的八样路径?

Cython无法识别Numpy类型