如何使用 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()
只是简单地包装它自己的 *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
虽然显然你失去了很多表达能力,因为现在你只能对你的过滤条件进行相等比较。当然,如果这就是您正在做的事情(对于更新插入可能是合理的),那没什么大不了的。
话虽如此,但总的来说,我建议您通读 why upsert is so complicated,因为除了获取查询的简单需求之外,您尝试执行此操作的方式中还有许多潜在的巨大陷阱条件正确。如果只有一个用户,在一个会话中工作,一次连接到您的数据库,这只会做您期望的事情。
我有一个运行良好的 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()
只是简单地包装它自己的 *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
虽然显然你失去了很多表达能力,因为现在你只能对你的过滤条件进行相等比较。当然,如果这就是您正在做的事情(对于更新插入可能是合理的),那没什么大不了的。
话虽如此,但总的来说,我建议您通读 why upsert is so complicated,因为除了获取查询的简单需求之外,您尝试执行此操作的方式中还有许多潜在的巨大陷阱条件正确。如果只有一个用户,在一个会话中工作,一次连接到您的数据库,这只会做您期望的事情。