8

我在 sqlalchemy 中广泛使用了 ORM 功能,因此在许多情况下,我已经从数据库中加载了数据,并且想要检查条件或对已加载的 python 对象执行计算;我还希望/需要做更多的面向批处理的任务,这些任务可以通过对数据库执行 sql 来更好地表达(并且根本不加载数据)。我想使用相同的代码来表达两种用途的相同计算,这样我就不必为数据库连接向后弯腰或将每个计算写两次(一次在常规 python 中,再次作为查询)并运行他们不同意的风险。

假设我有:

from sqlalchemy import Integer, Column
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Foo(Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)
    bar = Column(Integer)

bool_clause = Foo.bar > 10
int_clause = Foo.bar + 10

a_foo = Foo(bar=5)

有没有办法得到

>>> something(bool_clause, a_foo)
False
>>> something(int_clause, a_foo)
15

没有先持久a_foo化到数据库然后执行查询?我特别想要一种表达子句的方法,以便它可以在数据库查询的上下文中使用,但没有它仍然有用。

一种选择是将子句更改为函数:

bool_clause = lambda foo=Foo: foo.bar > 10
int_clause = lambda foo=Foo: foo.bar + 10
>>> bool_clause(a_foo)
False
>>> int_clause(a_foo)
15

但我发现这比表达条款的原始方式可读性差。

4

1 回答 1

6

有几种方法可以处理这种事情。

一种方法是正面的,Query.update() 和 Query.delete() 方法使用 SQLAlchemy 中的一个模块,该模块称为 sqlalchemy.orm.evaluator。它只能表达一组非常有限的表达式运算符:

>>> from sqlalchemy.orm.evaluator import EvaluatorCompiler
>>> print EvaluatorCompiler().process(bool_clause)(a_foo)
False
>>> print EvaluatorCompiler().process(int_clause)(a_foo)
15

它不会执行更复杂的表达式,例如,in_()如果您愿意,我们愿意将任何数量的合理操作添加到此模块中。

现在,您通常使用的用例方式是使用混合属性和方法。在这个用例中,我们利用了 Python 的优势,当我们拥有 时<anything>.someattr <some operator> <somethingelse>,我们可以换出selfclsfor <anything>。所以你的例子是:

class Foo(Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)
    bar = Column(Integer)

    @hybrid_method
    def bool_clause(self, other):
        return self.bar > other

    @hybrid_method
    def int_clause(self, other):
        return self.bar + other

>>> a_foo = Foo(bar=5)
>>> print a_foo.bool_clause(10)
False
>>> print a_foo.int_clause(10)
15

这实际上与您使用 lambdas 的建议相同,只是表达得更好。

这两种方法也可以结合使用。求值器的一个好处是它可以处理像or_()and之类的连词and_()。没有这个,如果您需要使用“and”或“or”之类的东西,混合要求您将方法分解为“实例”和“表达式”方法。

于 2013-01-12T20:13:04.553 回答