Skip to main content
 首页 » 编程设计

python之如何使用动态关系映射 SQLAlchemy 中的层次结构

2025年05月04日10shangdawei

我正在定义一个像这样的 SQLAlchemy 模型:

class SubProject(Base): 
  active = Column(Boolean) 
 
class Project(Base): 
  active = Column(Boolean) 
  subprojects = relationship(SubProject, backref=backref('project')) 
 
class Customer(Base): 
  active = Column(Boolean) 
  projects = relationship(Project, backref=backref('customer')) 

我需要获取处于这两种状态之一的客户列表:

  1. 所有客户,所有项目和所有子项目
  2. 只有活跃的客户,只有活跃的项目,只有活跃的子项目

    编辑 值得注意的是,所有没有项目的活跃客户都应该包括在内, 所有没有活跃调查的活跃项目都应该包括在内。

这在带有连接的 SQL 中是微不足道的,但我不知道如何使用 SQLAlchemy ORM 来完成它。这里的解决方案是什么?

请您参考如下方法:

如果我没理解错的话,您需要让所有非事件对象对您的查询不可见。以下类将过滤掉所有 active 属性设置为 False 的模型对象,包括那些通过关系访问的模型对象:

from sqlalchemy.orm import Query 
from sqlalchemy.orm.util import _class_to_mapper 
 
 
class QueryActive(Query): 
 
    def __init__(self, entities, *args, **kwargs): 
        Query.__init__(self, entities, *args, **kwargs) 
        query = self 
        for entity in entities: 
            if hasattr(entity, 'parententity'): 
                entity = entity.parententity 
            cls = _class_to_mapper(entity).class_ 
            if hasattr(cls, 'active'): 
                query = query.filter(cls.active==True) 
        self._criterion = query._criterion 
 
    def get(self, ident): 
        # Use default implementation when there is no condition 
        if not self._criterion: 
            return Query.get(self, ident) 
        # Copied from Query implementation with some changes. 
        if hasattr(ident, '__composite_values__'): 
            ident = ident.__composite_values__() 
        mapper = self._only_mapper_zero( 
                    "get() can only be used against a single mapped class.") 
        key = mapper.identity_key_from_primary_key(ident) 
        if ident is None: 
            if key is not None: 
                ident = key[1] 
        else: 
            from sqlalchemy import util 
            ident = util.to_list(ident) 
        if ident is not None: 
            columns = list(mapper.primary_key) 
            if len(columns)!=len(ident): 
                raise TypeError("Number of values doesn't match number " 
                                'of columns in primary key') 
            params = {} 
            for column, value in zip(columns, ident): 
                params[column.key] = value 
            return self.filter_by(**params).first() 

要使用它,您必须创建一个单独的 session 对象:

session_active = sessionmaker(bind=engine, query_cls=QueryActive)() 

这种方法有局限性,不适用于一些复杂的查询,但对于大多数项目来说是可以的。