SqlAlchemy - фильтрация по атрибуту отношения

95

У меня нет большого опыта работы с SQLAlchemy, и у меня есть проблема, которую я не могу решить. Я пробовал искать и пробовал много кода. Это мой класс (сокращенный до наиболее значимого кода):

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)

и я хотел бы опросить всех пациентов, у которых феноскор матери (например) == 10

Как уже было сказано, я перепробовал много кода, но не понял. Логическим решением, на мой взгляд, было бы

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

потому что .mother.phenoscoreпри выводе вы можете получить доступ к каждому элементу, но этот код этого не делает.

Есть ли (прямая) возможность фильтрации по атрибуту отношения (без написания оператора SQL или дополнительного оператора соединения), мне нужен этот вид фильтра более одного раза.

Даже если нет простого решения, я рад получить все ответы.

user1105851
источник

Ответы:

173

Используйте метод has()связи (более читаемый):

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

или присоединяйтесь (обычно быстрее):

patients = Patient.query.join(Patient.mother, aliased=True)\
                    .filter_by(phenoscore=10)
Денис Откидач
источник
9
пациенты = Patient.query.filter (Patient.mother.has (Patient.phenoscore == 10))
user1105851,
@ user1105851 has()поддерживает как выражение условия как безымянный аргумент, так и filter_byаргументы ключевого слова -style . Последнее кажется мне более читаемым.
Денис Откидач
@DenisOtkidach правильно, но тогда было бы phenoscore = 10. filter_byпринимает только ключевые слова равенства (так как он просто делает с ними ** kwargs)
aruisdante 01
@aruisdante Вы правы, это была ошибочная редакция ответа.
Денис Откидач 02
5
используйте вместо этого любые : members = Patient.query.filter (Patient.mother.any (phenoscore = 10))
Бостон Кенн,
7

Я использовал его с сеансами, но альтернативный способ прямого доступа к полю отношений -

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

Я не тестировал, но думаю, это тоже сработает

Patient.query.join(Patient.mother) \
    .filter(Patient.mother.property.mapper.class_.phenoscore==10)
Finch_Powers
источник
5

Хорошие новости для вас: я недавно сделал пакет, который дает вам фильтрацию / сортировку с "волшебными" строками, как в Django , так что теперь вы можете написать что-то вроде

Patient.where(mother___phenoscore=10)

Это намного короче, особенно для сложных фильтров, например,

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

Надеюсь, вам понравится этот пакет

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

Александр Литвиненко
источник
1

Это более общий ответ о том, как запрашивать отношения.

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

Это позволяет вам:

parent_obj.some_relationship.filter(ParentClass.some_attr==True).all()
Джеймс
источник