如何使用 OR 和 SQLAlchemy 制作 get_or_create?

How to make a get_or_create using OR with SQLAlchemy?

我有一个运行良好的 get 或 create 方法,但它 AND 是参数,使用 filter_by。我想要同样的东西,但使用或类似于:

def get_or_create_or(cls, session, **kwargs):
    instance = session.query(cls).filter(or_(**kwargs)).first()
    if instance:
        return instance
    else:
        instance = cls(**kwargs)
        return instance

这不起作用,因为 or_ 不需要关键字参数。我怎样才能制作这个的通用版本?

例如,我当前的get_or_create可以接收phone和一封电子邮件,并且return会匹配两者的行。我希望能够传入 phone 和一封电子邮件,并获得匹配的行。

你的问题不是 or_ 不期望关键字参数,而是关键字参数首先传递它并不是一件有意义的事情。 filter()and_()or_() 都需要一个或多个 BinaryExpression 来定义复合表达式(实际上 filter() 只是简单地包装它自己的 *argsand_(*args) 中允许隐式默认和'ing)。您的方法签名应该是:

def get_or_create_or(cls, session, *args):
    instance = session.query(cls).filter(or_(*args)).first()
    # rest of stuff

它应该这样称呼:

>>> get_or_create_or(MyClass, session, MyClass.attribute == "foo", MyClass.attribute_other > 2)

您的困惑可能来自使用 filter_by,它隐含地只允许进行相等比较,因此使用关键字参数是有道理的。遗憾的是,无论出于何种原因,都没有 filter_by 的 OR'ing 版本。您可以通过以下方式伪造它:

def or_filter_by_conditions(cls, **kwargs):
    return or_(getattr(cls, name) == value for name, value in kwargs.iteritems()) # python2
    return or_(getattr(cls, name) == value for name, value in kwargs.items()) # python3

将你原来的方法变成:

def get_or_create_or(cls, session, **kwargs):
    instance = session.query(cls).filter(or_filter_by_conditions(**kwargs)).first()
    # rest

虽然显然你失去了很多表达能力,因为现在你只能对你的过滤条件进行相等比较。当然,如果这就是您正在做的事情(对于更新插入可能是合理的),那没什么大不了的。

话虽如此,但总的来说,我建议您通读 why upsert is so complicated,因为除了获取查询的简单需求之外,您尝试执行此操作的方式中还有许多潜在的巨大陷阱条件正确。如果只有一个用户,在一个会话中工作,一次连接到您的数据库,这只会做您期望的事情。