SqlAlchemy - Filtrage par attribut de relation

94

Je n'ai pas beaucoup d'expérience avec SQLAlchemy et j'ai un problème que je ne peux pas résoudre. J'ai essayé de chercher et j'ai essayé beaucoup de code. Ceci est ma classe (réduite au code le plus significatif):

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)

et je voudrais interroger tous les patients, dont le phénoscore de la mère est (par exemple) == 10

Comme dit, j'ai essayé beaucoup de code, mais je ne comprends pas. La solution logique, à mes yeux, serait

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

car, vous pouvez accéder .mother.phenoscoreà chaque élément lors de la sortie, mais ce code ne le fait pas.

Y a-t-il une possibilité (directe) de filtrer par un attribut d'une relation (sans écrire l'instruction SQL, ou une déclaration de jointure supplémentaire), j'ai besoin de ce type de filtre plus d'une fois.

Même s'il n'y a pas de solution facile, je suis heureux d'obtenir toutes les réponses.

user1105851
la source

Réponses:

169

Utilisez la méthode has()de relation (plus lisible):

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

ou rejoindre (généralement plus rapide):

patients = Patient.query.join(Patient.mother, aliased=True)\
                    .filter_by(phenoscore=10)
Denis Otkidach
la source
9
patients = Patient.query.filter (Patient.mother.has (Patient.phenoscore == 10))
user1105851
@ user1105851 has()prend en charge à la fois l'expression de condition en tant qu'argument sans nom et filter_byarguments de mot-clé -style. Ce dernier me semble plus lisible.
Denis Otkidach
@DenisOtkidach est correct, mais ce serait le cas phenoscore = 10. filter_byne prend que les mots-clés d'égalité (car il fait juste ** kwargs sur eux)
aruisdante
@aruisdante Vous avez raison, c'était une modification erronée de la réponse.
Denis Otkidach
4
utiliser tout lieu: patients = Patient.query.filter (Patient.mother.any (phenoscore = 10))
Boston Kenne
7

Je l'ai utilisé avec des sessions, mais une autre façon d'accéder directement au champ de relation est

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

Je ne l'ai pas testé, mais je suppose que cela fonctionnerait aussi

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

Bonne nouvelle pour vous: j'ai récemment créé un package qui vous permet de filtrer / trier avec des chaînes "magiques" comme dans Django , vous pouvez donc maintenant écrire quelque chose comme

Patient.where(mother___phenoscore=10)

C'est beaucoup plus court, en particulier pour les filtres complexes, disons,

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

J'espère que vous apprécierez ce forfait

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

Alexandre Litvinenko
la source
0

Il s'agit d'une réponse plus générale sur la façon d'interroger les relations.

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

Cela vous permet de:

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