如何在SQLAlchemy混合表达式中实现关联过滤及获取会话?
首先,咱们先明确核心问题:你已经在Run类里用hybrid_property实现了实例层面的default_region判断,但要在查询中用这个属性过滤时,需要补全对应的expression方法,同时你疑惑怎么在这个类方法里获取会话——其实根本不需要在expression里获取会话,因为expression是用来生成SQL表达式的,不是操作具体实例,咱们换个思路从SQL关联的角度来解决。
方法一:补全@default_region.expression的正确实现
要实现类级别的过滤,咱们可以用SQL的EXISTS子查询,检查当前Run对应的Tag集合中是否存在符合条件的记录。代码如下:
from sqlalchemy import exists, and_ @default_region.expression def default_region(cls): return exists().where( and_( Tag.run_id == cls.id, Tag.name == "region", Tag.value == "default" ) )
这个表达式会告诉SQLAlchemy生成一个子查询,判断每条Run记录是否存在关联的Tag满足name='region'且value='default',这样你原来的查询session.query(Run).filter(Run.default_region == True).all()就能正常执行了,完全不需要手动获取会话。
方法二:更直接的查询过滤方式(无需hybrid expression)
如果你觉得写hybrid的expression有点麻烦,也可以直接在查询语句里用关联查询来过滤,有两种常用方式:
方式A:JOIN + DISTINCT去重
因为Run和Tag是一对多关联,直接join会返回重复的Run记录,所以用distinct去重:
session.query(Run).join(Run.tag_collection).filter( Tag.name == "region", Tag.value == "default" ).distinct().all()
方式B:直接用EXISTS子查询
和hybrid expression的原理一致,但直接写在查询里更直观:
from sqlalchemy import exists, and_ subquery = exists().where( and_( Tag.run_id == Run.id, Tag.name == "region", Tag.value == "default" ) ) session.query(Run).filter(subquery).all()
补充说明:为什么不需要获取会话?
你之前纠结的会话问题其实是个误区——Session.object_session()是用来获取某个实例所属的会话,但expression是类级别的方法,它针对的是数据库表结构,用来生成SQL片段,SQLAlchemy在执行查询时会自动把这个片段整合到最终的SQL语句中,完全不需要手动操作会话。
内容的提问来源于stack exchange,提问作者nPn

