如何使用SQLAlchemy使用OR创建get_or_create?

时间:2015-03-16 18:06:15

标签: python 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可以接收电话和电子邮件,并返回与两者匹配的行。我希望能够通过电话和电子邮件获得与之匹配的行。

1 个答案:

答案 0 :(得分:2)

你的问题不是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

虽然你现在显然失去了很多表现力,但现在你只能对你的过滤条件进行相等比较。当然,如果这是你正在做的事情(对于upsert可能是合理的),那么这不是什么大问题。

话虽如此,一般来说,我建议您阅读why upsert is so complicated,因为除了简单的查询条件正确需要之外,还有许多潜在的巨大问题。 。如果只有一个用户在一个会话中工作,一次只能连接到您的数据库,那么这只能达到预期效果。