SqlAlchemy - Filtrando por atributo de relacionamento

93

Não tenho muita experiência com SQLAlchemy e estou com um problema que não consigo resolver. Tentei pesquisar e tentei muitos códigos. Esta é minha classe (reduzida ao código mais significativo):

class Patient(Base):
    __tablename__ = 'patients'
    id = Column(Integer, primary_key=True, nullable=False)
    mother_id = Column(Integer, ForeignKey('patients.id'), index=True)
    mother = relationship('Patient', primaryjoin='Patient.id==Patient.mother_id', remote_side='Patient.id', uselist=False)
    phenoscore = Column(Float)

e gostaria de consultar todos os pacientes, cujo phenoscore da mãe é (por exemplo) == 10

Como disse, tentei muitos códigos, mas não entendi. A solução lógica, a meu ver, seria

patients = Patient.query.filter(Patient.mother.phenoscore == 10)

porque, você pode acessar .mother.phenoscorepara cada elemento durante a saída, mas, este código não faz isso.

Existe a possibilidade (direta) de filtrar por um atributo de um relacionamento (sem escrever a instrução SQL ou uma instrução extra de junção), preciso desse tipo de filtro mais de uma vez.

Mesmo que não haja uma solução fácil, fico feliz em obter todas as respostas.

user1105851
fonte

Respostas:

167

Use método has()de relacionamento (mais legível):

patients = Patient.query.filter(Patient.mother.has(phenoscore=10))

ou junte-se (geralmente mais rápido):

patients = Patient.query.join(Patient.mother, aliased=True)\
                    .filter_by(phenoscore=10)
Denis Otkidach
fonte
9
pacientes = Paciente.query.filter (Paciente.mãe.has (Paciente.phenoscore == 10))
usuário1105851
@ user1105851 has()suporta expressão de condição como argumento sem nome e filter_byargumentos de palavra-chave -style. O último parece mais legível para mim.
Denis Otkidach
@DenisOtkidach correto, mas então seria phenoscore = 10. filter_bysó aceita palavras-chave de igualdade (já que está apenas fazendo ** kwargs nelas)
aruisdante 01 de
@aruisdante Você tem razão, foi uma edição errônea da resposta.
Denis Otkidach
4
em vez disso, use qualquer : patient = Patient.query.filter (Patient.mother.any (phenoscore = 10))
Boston Kenne
12

Você tem que consultar a relação com o join

Você obterá o exemplo deste Auto-Referential Query Strategies

Nilesh
fonte
1
O link não existe.
ciberbesta de
7

Eu usei com sessões, mas uma forma alternativa de acessar o campo de relacionamento diretamente é

db_session.query(Patient).join(Patient.mother) \
    .filter(Patient.mother.property.mapper.class_.phenoscore==10)

Não testei, mas acho que também funcionaria

Patient.query.join(Patient.mother) \
    .filter(Patient.mother.property.mapper.class_.phenoscore==10)
Finch_Powers
fonte
5

Boas notícias para você: recentemente fiz um pacote que fornece filtragem / classificação com strings "mágicas" como no Django , então agora você pode escrever algo como

Patient.where(mother___phenoscore=10)

É muito mais curto, especialmente para filtros complexos, digamos,

Comment.where(post___public=True, post___user___name__like='Bi%')

Espero que você goste deste pacote

https://github.com/absent1706/sqlalchemy-mixins#django-like-queries

Alexander Litvinenko
fonte
0

Esta é uma resposta mais geral sobre como consultar relacionamentos.

relationship(..., lazy='dynamic', ...)

Isso permite que você:

parent_obj.some_relationship.filter(ParentClass.some_attr==True).all()
James
fonte