开发者

filtering by association attributes with SqlAlchemy association_proxy

开发者 https://www.devze.com 2023-02-01 19:05 出处:网络
I have a many-to-many (developers & projects) relationship modeled using SA\'s association_proxy. The collections (developers on each project & projects for each developer) work fine, but I ne

I have a many-to-many (developers & projects) relationship modeled using SA's association_proxy. The collections (developers on each project & projects for each developer) work fine, but I need to filter on an attribute of the association itself (status). Something like this (which does not work):

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

What am I missing? Here is the complete test code:

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':'stackoverflow'},
    {'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'


Use any() method of association proxy:

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


To add to Denis's answer:

When I tried the any() method, I got this error

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

Using has() instead of any() worked for me.

It looks like any() is for list relationships (in this case Developers potentially have multiple projects so Developer.developerProjects is a list). Whereas has() is for a single relationship (for instance if a Developer had one Workplace associated).

Here is the link to documentation: http://docs.sqlalchemy.org/en/latest/orm/internals.html#sqlalchemy.orm.properties.RelationshipProperty.Comparator.has

0

精彩评论

暂无评论...
验证码 换一张
取 消