使用 SqlAlchemy association_proxy 按关联属性过滤

Posted

技术标签:

【中文标题】使用 SqlAlchemy association_proxy 按关联属性过滤【英文标题】:filtering by association attributes with SqlAlchemy association_proxy 【发布时间】:2011-06-02 03:24:31 【问题描述】:

我有一个使用 SA 的 association_proxy 建模的多对多(开发人员和项目)关系。集合(每个项目的开发人员和每个开发人员的项目)工作正常,但我需要过滤关联本身的属性(状态)。像这样的东西(不起作用):

activeDevelopers = s.query(Developer).filter_by(Developer.developerProjects.status == 'active').all()

我错过了什么? 以下是完整的测试代码:

import logging
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.orm import relation, mapper, sessionmaker
from sqlalchemy.sql import *
from sqlalchemy.ext.associationproxy import association_proxy

log = logging.getLogger('nm_test')
logging.basicConfig(level=logging.DEBUG,
        format='%(asctime)s,%(msecs)03d %(levelname)s [%(filename)s.%(funcName)s @ %(lineno)d.%(thread)d] %(message)s')

engine = create_engine('sqlite:///:memory:', echo = False, echo_pool = False)
meta = MetaData()
meta.bind = engine

developer_table = Table('developer', meta,
    Column('id', Integer, primary_key=True, autoincrement = False),
    Column('name', String),
)

project_table = Table('project', meta,
    Column('id', Integer, primary_key=True, autoincrement = True),
    Column('name', String)
)

developer_project_table = Table('developer_project', meta,
    Column('developer_id', Integer, ForeignKey('developer.id'), primary_key = True),
    Column('project_id', Integer, ForeignKey('project.id'), primary_key = True),
    Column('status', String)
)

class Developer(object):
    projects = association_proxy('developerProjects', 'projects')
    def __str__(self):
        return 'Developer id:%i, name:%s' % (self.id, self.name)

class Project(object):
    developers = association_proxy('developerProjects', 'developers')
    def __str__(self):
        return 'Project id:%i, name:%s' % (self.id, self.name)

class DeveloperProject(object):
    def __str__(self):
        return 'DeveloperProject developer:%s, project:%s, status:%s' % (self.developer_id, self.project_id, self.status)

mapper(Developer, developer_table, properties = 
    'developerProjects':relation(DeveloperProject, backref = "developers")
)

mapper(Project, project_table, properties = 
    'developerProjects':relation(DeveloperProject, backref = "projects")
)

mapper(DeveloperProject, developer_project_table)

meta.create_all(engine)
conn = engine.connect()

conn.execute(project_table.insert(),[
    'name':'***',
    'name':'superuser',
])
conn.execute(developer_table.insert(),[
    'name':'John',
    'name': 'TerryJ',
    'name': 'TerryG',
    'name': 'Eric',
    'name': 'Graham',
])
conn.execute(developer_project_table.insert(),[
    'developer_id':1, 'project_id':1, 'status':'active',
    'developer_id':2, 'project_id':2, 'status':'inactive',
    'developer_id':3, 'project_id':2, 'status':'active',
    'developer_id':4, 'project_id':1, 'status':'active',
    'developer_id':4, 'project_id':2, 'status':'active',
    'developer_id':5, 'project_id':1, 'status':'active',
    'developer_id':5, 'project_id':2, 'status':'inactive',
])

Session = sessionmaker(bind=engine)
s = Session()

developers = s.query(Developer).all()
projects = s.query(Project).all()

for d in developers:
    log.debug(d)
    for p in d.projects:
        log.debug('    %s' % p)

for p in projects:
    log.debug(p)
    for d in p.developers:
        log.debug('    %s' % d)

# does not work
activeDevelopers = s.query(Developer).filter_by(Developer.developerProjects.status == 'active').all()
# AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object has an attribute 'status'

【问题讨论】:

【参考方案1】:

使用any() method的关联代理:

s.query(Developer).filter(Developer.developerProjects.any(status='active'))

【讨论】:

这种场景下如何使用like进行匹配?【参考方案2】:

补充丹尼斯的答案:

当我尝试any() 方法时,我得到了这个错误

sqlalchemy.exc.InvalidRequestError: 'any()' not implemented for scalar attributes. Use has().

使用 has() 而不是 any() 对我有用。

看起来any() 用于列表关系(在这种情况下,开发人员可能有多个项目,因此Developer.developerProjects 是一个列表)。而has() 用于单个关系(例如,如果Developer 有一个Workplace 关联)。

这里是文档的链接:http://docs.sqlalchemy.org/en/latest/orm/internals.html#sqlalchemy.orm.properties.RelationshipProperty.Comparator.has

【讨论】:

以上是关于使用 SqlAlchemy association_proxy 按关联属性过滤的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQLAlchemy 中正确添加数据?

SQLAlchemy:查询所有没有直接关系的对象

sqlalchemy中的多对多自引用关系

SQLAlchemy InvalidRequest 添加到关系集合

SQLAlchemy:创建多对多并填充关联

在 SQLAlchemy 中,当有多个关系时,如何访问查询中的引用属性?