我有一个运行良好的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可以接收电话和电子邮件,并返回与两者匹配的行。我希望能够通过电话和电子邮件获得与之匹配的行。
答案 0 :(得分:2)
你的问题不是or_
不期望关键字参数,而是关键字参数首先传递它并不是一件好事。 filter()
,and_()
和or_()
都期望定义复合表达式的一个或多个BinaryExpression
(实际上filter()
只包含自己的*args
在and_(*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,因为除了简单的查询条件正确需要之外,还有许多潜在的巨大问题。 。如果只有一个用户在一个会话中工作,一次只能连接到您的数据库,那么这只能达到预期效果。