首页 > 解决方案 > 如何在 sqlalchemy 混合表达式中运行查询?

问题描述

有一个与类标签有“多”关系的类 Run。我需要能够根据标签(tag_collection)的功能过滤运行。

下面是我的代码片段。在这种情况下,我使用的是自动映射器,但我认为这并不重要。

Base = automap_base()

class Run(Base):
    __tablename__ = 'runs'
    macro_id = Column(ForeignKey('macros.id'))  

    @hybrid_property
    def tag_count(self):
        return(len(self.tag_collection))

    @hybrid_property
    def default_region(self):
        return(reduce(lambda  memo, t: memo or t.name == "region" and t.value == "default", self.tag_collection, False))

    @default_region.expression
    def default_region(cls):
        # how do I do the query here?

class Tag(Base):
    __tablename__ = 'tags'
    run_id = Column(ForeignKey('runs.id'))    

如果我得到一个 Run 的实例,我可以使用该default_region属性,但是当我运行一个查询并尝试使用该属性进行过滤时,我要么在装饰方法default_region中获取传入的 Run 类(作为自身),要么我最终出现在装饰方法中,该方法也接收 Run 类。@hybird_propertydefault_region@default_region.expressiondefault_region

示例查询:

session.query(Run).filter(Run.default_region == True).all()

我想我需要在@default_region.expression装饰default_region方法中进行查询,但我看不到如何获取会话句柄。我知道Session.object_session(someobject),但我认为someobject需要是 orm 类的实例而不是类本身。

我认为我的过滤方法完全错误,或者我需要以某种方式在该@default_region.expression装饰default_region方法中获取会话对象的句柄。

有没有更好的方法来完成这种过滤?

如何在@default_region.expression装饰default_region方法中获取会话?

标签: pythonsqlalchemy

解决方案


这在我看来是Correlated Subquery Relationship Hybrid。当Run.default_region被添加为过滤器时,您要更新要添加的查询

WHERE EXISTS (
    SELECT 1 FROM tags
    WHERE run_id = runs.id
      AND name = "region"
      AND value = "default")

exists()使用函数将其作为表达式返回:

from sqlalchemy.sql import exists, and_

@default_region.expression
def default_region(cls):
    return exists([1]).where(and_(
        Tag.run_id == cls.id,
        Tag.name == 'region',
        Tag.value == 'default'))

注意这里要避免使用== Trueor == False,没必要!只需直接使用该属性:

session.query(Run).filter(Run.default_region).all()

或用于~否定:

session.query(Run).filter(~Run.default_region).all()

通过定义属性的.expression方法,您已经告诉 SQLAlchemy 在访问类上的属性时使用该方法,因此上下文始终是那里的类。如果没有定义.exists(或 a .comparator),Run.default_region将调用default_regiongetter(用 `@hybrid_property 装饰的方法)作为类方法,因此只有这样该方法才必须与传入的实例类一起使用。对于某些混合属性实现,这恰好取决于它们如何在上下文中使用属性。

至于default_region 实例属性的实现,我不会reduce()在那里使用;使用any()功能

@hybrid_property
def default_region(self):
    return any(
        t.name == "region" and t.value == "default"
        for t in self.tag_collection)

any()找到匹配项时停止迭代提供的生成器表达式。


推荐阅读