如何在SQLAlchemy中使用FROM对象注释函数元素

Ilj*_*ilä 17 python postgresql sqlalchemy

在回答另一个问题时,我试图生成一个混合属性,它会返回一个包含标量子查询的函数表达式,该子标点将所有FROM对象与封闭查询相关联,但如果没有其他表达式,它仍会提供一个FROM对象.封闭查询提供要关联的表.

给出玩具模型的定义

class Foo(Base):
    __tablename__ = 'foo'

    foo_id = Column(Integer, primary_key=True, autoincrement=True)
    bar = Column(postgresql.ARRAY(Integer))
    baz = Column(postgresql.ARRAY(Integer))

    @hybrid_property
    def bar_sans_baz(self):
        return list(set(self.bar).difference(self.baz))

    @bar_sans_baz.expression
    def bar_sans_baz(cls):
        bar = func.unnest(cls.bar).select().correlate(cls)
        baz = func.unnest(cls.baz).select().correlate(cls)
        stmt = bar.except_(baz)
        # Uses `func` generic as ARRAY() constructor
        return func.array(stmt.as_scalar(),
                          type_=postgresql.ARRAY(Integer))
Run Code Online (Sandbox Code Playgroud)

问题是

session.query(Foo.bar_sans_baz)
Run Code Online (Sandbox Code Playgroud)

结果是

SELECT array((SELECT unnest(foo.bar) AS unnest_1 
              FROM foo EXCEPT SELECT unnest(foo.baz) AS unnest_2 
              FROM foo)) AS bar_sans_baz
Run Code Online (Sandbox Code Playgroud)

因为它在封闭查询中没有任何关联.一个简单的补救方法是使用select_from()显式添加FROM对象:

session.query(Foo.bar_sans_baz).select_from(Foo)
Run Code Online (Sandbox Code Playgroud)

并且得到的查询是期望的

SELECT array((SELECT unnest(foo.bar) AS unnest_1
              EXCEPT SELECT unnest(foo.baz) AS unnest_2)) AS bar_sans_baz 
FROM foo
Run Code Online (Sandbox Code Playgroud)

虽然这是有效的,但它增加了心理开销,因为你必须记住添加select_from(Foo),如果只选择混合属性.这不是问题,如果任何其他选择项提供Foo:

In [105]: print(session.query(Foo.bar_sans_baz, Foo.foo_id))
SELECT array((SELECT unnest(foo.bar) AS unnest_1 EXCEPT SELECT unnest(foo.baz) AS unnest_2)) AS bar_sans_baz, foo.foo_id AS foo_foo_id 
FROM foo
Run Code Online (Sandbox Code Playgroud)

有没有办法注释返回的函数表达式func.array(...)- 或内部标量子查询 - 以便它将提供Foo作为FROM对象,如果没有其他表达式提供它.或者换句话说,只是方式来增加作为from_obj给函数表达式或标量子查询,使得内选择可以使用.

sop*_*ros 1

我想知道为什么你不在定义.select_from中明确添加expression

@bar_sans_baz.expression
def bar_sans_baz(cls):
    bar = func.unnest(cls.bar).select().correlate(cls)
    baz = func.unnest(cls.baz).select().correlate(cls)
    stmt = bar.except_(baz).select_from(cls.__tablename__)
    # Uses `func` generic as ARRAY() constructor
    return func.array(stmt.as_scalar(),
                      type_=postgresql.ARRAY(Integer))
Run Code Online (Sandbox Code Playgroud)