如何在SQLAlchemy中映射具有动态关系的层次结构?
我正在定义一个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'))
我需要获取客户的列表,满足以下两种状态之一:
- 所有客户,包括所有项目和所有子项目
只有活跃的客户,包括只有活跃的项目和只有活跃的子项目
补充说明 特别要注意的是,所有没有项目的活跃客户也应该被包括在内,所有没有活跃调查的活跃项目也应该被包括在内。
如果用SQL语句来做这件事会很简单,只需要用到连接操作,但我不知道如何用SQLAlchemy的ORM来实现。有什么解决办法吗?
2 个回答
0
为了补充Denis的回答,你可以使用enable_assertions(False)。根据我的理解,这个功能是对SQLAlchemy在正常操作中添加的查询进行额外检查。如果遇到更复杂的情况,你可以把它关闭。
0
如果我理解得没错,你的意思是希望所有不活跃的对象在查询时变得不可见。下面这个类会过滤掉所有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_active = sessionmaker(bind=engine, query_cls=QueryActive)()
这种方法有一些限制,对于某些复杂的查询可能不适用,但对于大多数项目来说是可以的。