SqlAlchemy - filtrowanie według atrybutu relacji

95

Nie mam dużego doświadczenia z SQLAlchemy i mam problem, którego nie mogę rozwiązać. Próbowałem szukać i próbowałem dużo kodu. To jest moja klasa (zredukowana do najbardziej znaczącego kodu):

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)

i chciałbym zapytać wszystkich pacjentów, których fenoscore matki jest (na przykład) == 10

Jak powiedziano, próbowałem dużo kodu, ale go nie rozumiem. Moim zdaniem logicznym rozwiązaniem byłoby

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

ponieważ możesz uzyskać dostęp .mother.phenoscoredo każdego elementu podczas wyprowadzania, ale ten kod tego nie robi.

Czy istnieje (bezpośrednia) możliwość filtrowania według atrybutu relacji (bez pisania instrukcji SQL lub dodatkowej instrukcji łączenia), potrzebuję tego rodzaju filtra więcej niż jeden raz.

Nawet jeśli nie ma prostego rozwiązania, z przyjemnością otrzymuję wszystkie odpowiedzi.

user1105851
źródło

Odpowiedzi:

172

Użyj metody has()relacji (bardziej czytelne):

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

lub dołącz (zwykle szybciej):

patients = Patient.query.join(Patient.mother, aliased=True)\
                    .filter_by(phenoscore=10)
Denis Otkidach
źródło
9
pacjentów = Patient.query.filter (Patient.mother.has (Patient.phenoscore == 10))
user1105851
@ user1105851 has()obsługuje zarówno wyrażenie warunku jako nienazwany argument, jak i argumenty filter_by-stylowe słowa kluczowe. To później wydaje mi się bardziej czytelne.
Denis Otkidach,
@DenisOtkidach poprawne, ale wtedy byłoby phenoscore = 10. filter_byprzyjmuje tylko słowa kluczowe równości (ponieważ robi na nich tylko ** kwargs)
aruisdante
@aruisdante Masz rację, to była błędna edycja odpowiedzi.
Denis Otkidach
4
używać jakichkolwiek zamiast: pacjenci = Patient.query.filter (Patient.mother.any (phenoscore = 10))
Boston Kenne
7

Używałem go z sesjami, ale alternatywnym sposobem bezpośredniego dostępu do pola relacji jest

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

Nie testowałem tego, ale myślę, że to też zadziała

Patient.query.join(Patient.mother) \
    .filter(Patient.mother.property.mapper.class_.phenoscore==10)
Finch_Powers
źródło
5

Dobra wiadomość dla Ciebie: Niedawno stworzyłem pakiet, który daje Ci filtrowanie / sortowanie za pomocą „magicznych” łańcuchów znaków, jak w Django , więc możesz teraz napisać coś takiego

Patient.where(mother___phenoscore=10)

Jest dużo krótszy, zwłaszcza w przypadku złożonych filtrów, np.

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

Mam nadzieję, że ten pakiet Ci się spodoba

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

Aleksandra Litwinienko
źródło
1

To jest bardziej ogólna odpowiedź na temat wykonywania zapytań dotyczących relacji.

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

Dzięki temu możesz:

parent_obj.some_relationship.filter(ParentClass.some_attr==True).all()
James
źródło