2010-12-31 4 views
10

Ho un molti-a-molti (sviluppatori & progetti) rapporto modellata utilizzando del SA association_proxy. Le raccolte (gli sviluppatori per ogni progetto & per ogni sviluppatore) funzionano bene, ma devo filtrare su un attributo dell'associazione stessa (stato). Qualcosa di simile (che non funziona):filtraggio per associazione attributi con SQLAlchemy association_proxy

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

Cosa mi manca? Ecco il codice di prova completo:

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' 

risposta

9

Uso any() method di delega dell'associazione:

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

Grazie, funziona bene. – Hollister

+1

Come usare 'like' per la corrispondenza in questo scenario? –

4

Per aggiungere alla risposta di Denis:

Quando ho provato il metodo any(), ho ottenuto questo errore

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

utilizzando has() invece di any() ha funzionato per me.

Sembra che any() sia per le relazioni di elenco (in questo caso gli sviluppatori hanno potenzialmente più progetti, quindi Developer.developerProjects è un elenco). Considerando che has() è per una singola relazione (ad esempio se a era associato uno Workplace).

Ecco il link alla documentazione: http://docs.sqlalchemy.org/en/latest/orm/internals.html#sqlalchemy.orm.properties.RelationshipProperty.Comparator.has