SqlAlchemy: filtrado por atributo de relación

94

No tengo mucha experiencia con SQLAlchemy y tengo un problema que no puedo resolver. Intenté buscar y probé mucho código. Esta es mi clase (reducida al código más 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)

y me gustaría consultar a todos los pacientes, cuyo fenoscore de la madre es (por ejemplo) == 10

Como dije, probé mucho código, pero no lo entiendo. La solución lógica, en mi opinión, sería

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

porque puede acceder .mother.phenoscorea cada elemento al generar, pero este código no lo hace.

¿Existe una posibilidad (directa) de filtrar por un atributo de una relación (sin escribir la declaración SQL, o una declaración de unión adicional), necesito este tipo de filtro más de una vez.

Incluso si no hay una solución fácil, estoy feliz de obtener todas las respuestas.

usuario1105851
fuente

Respuestas:

169

Utilice el método has()de relación (más legible):

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

o únete (generalmente más rápido):

patients = Patient.query.join(Patient.mother, aliased=True)\
                    .filter_by(phenoscore=10)
Denis Otkidach
fuente
9
pacientes = Patient.query.filter (Patient.mother.has (Patient.phenoscore == 10))
user1105851
@ user1105851 has()admite tanto la expresión de condición como argumento sin nombre como los argumentos de filter_bypalabra clave -style. La última me parece más legible.
Denis Otkidach
@DenisOtkidach correcto, pero entonces lo sería phenoscore = 10. filter_bysolo toma palabras clave de igualdad (ya que solo les hace ** kwargs)
aruisdante
@aruisdante Tienes razón, fue edición errónea de la respuesta.
Denis Otkidach
4
use any en su lugar: pacientes = Patient.query.filter (Patient.mother.any (phenoscore = 10))
Boston Kenne
7

Lo usé con sesiones, pero una forma alternativa en la que puede acceder al campo de relación directamente es

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

No lo he probado, pero supongo que esto también funcionaría

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

Buenas noticias para ti: recientemente hice un paquete que te permite filtrar / ordenar con cadenas "mágicas" como en Django , por lo que ahora puedes escribir algo como

Patient.where(mother___phenoscore=10)

Es mucho más corto, especialmente para filtros complejos, digamos,

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

Espero que disfrutes de este paquete

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

Alexander Litvinenko
fuente
0

Esta es una respuesta más general sobre cómo consultar relaciones.

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

Esto le permite:

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