首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SQLAlchemy有没有和姜戈的get_or_create等价物?

SQLAlchemy有没有和姜戈的get_or_create等价物?
EN

Stack Overflow用户
提问于 2010-03-30 22:57:31
回答 10查看 75.7K关注 0票数 186

如果对象已经存在(基于提供的参数),我希望从数据库中获取该对象,如果不存在,则创建该对象。

Django的get_or_create (或source)可以做到这一点。在SQLAlchemy中有没有类似的快捷方式?

我现在明确地写出来,就像这样:

代码语言:javascript
运行
复制
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
EN

回答 10

Stack Overflow用户

回答已采纳

发布于 2010-04-07 01:47:07

这基本上就是做这件事的方式,AFAIK没有现成的捷径。

当然,您可以对其进行泛化:

代码语言:javascript
运行
复制
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+ )

这是一个带有Python3.9的the new dict union operator (|=)的更干净的版本

代码语言:javascript
运行
复制
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%的保证。

票数 121
EN

Stack Overflow用户

发布于 2011-05-21 06:08:42

遵循@WoLpH的解决方案,这是对我有效的代码(简单版本):

代码语言:javascript
运行
复制
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我的模型中的任何对象了。

假设我的模型对象是:

代码语言:javascript
运行
复制
class Country(Base):
    __tablename__ = 'countries'
    id = Column(Integer, primary_key=True)
    name = Column(String, unique=True)

为了获取或创建我的对象,我写道:

代码语言:javascript
运行
复制
myCountry = get_or_create(session, Country, name=countryName)
票数 127
EN

Stack Overflow用户

发布于 2014-01-16 03:30:24

我一直在尝试解决这个问题,最终得到了一个相当健壮的解决方案:

代码语言:javascript
运行
复制
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,但是有一些关于我为什么使用这个的相当多的想法。

  1. 它解压成一个元组,告诉你对象是否存在。这在您的工作流程中通常很有用。
  2. 该函数使您能够使用@classmethod修饰的创建者函数(以及特定于这些函数的属性)。
  3. 当您有多个进程连接到数据存储时,该解决方案可防止出现竞争情况。

编辑:我已经按照this blog post中的解释将session.commit()更改为session.flush()。请注意,这些决策特定于所使用的数据存储(在本例中为Postgres)。

编辑2:我已经在函数中使用{}作为默认值进行了更新,因为这是典型的Python陷阱。谢谢你的the comment,奈杰尔!如果你对这个问题很好奇,可以看看this StackOverflow questionthis blog post

票数 56
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2546207

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档