Sqlalchemy: Consulta sobre relaciones m2m para clases polimórficas

Tengo dos clases que están conectadas a través de una relación de muchos a muchos: Parent y Tag .

 Base = declarative_base() association_table = Table('associations', Base.metadata, Column('parent_id', Integer, ForeignKey('parent.id')), Column('tag_id', Integer, ForeignKey('tag.id')), ) class Tag(Base): __tablename__ = 'tags' id = Column(Integer, Sequence('tag_id_seq'), primary_key=True) name = Column(String) class Parent(Base): __tablename__ = 'parents' id = Column(Integer, Sequence('parent_id_seq'), primary_key=True) tags = relationship('Tag', secondary=association_table, backref='parents') 

Si quiero consultar todos los objetos de Tag que tienen una o más relaciones con un Parent , lo haría:

 session.query(Tag).filter(Tag.parents.any()).all() 

Sin embargo, esta clase de padres es de padres a hijos, Alice y Bob :

 class Alice(Parent): __tablename__ = 'alices' __mapper_args__ = {'polymorphic_identity': 'alice'} alice_id = Column(Integer, ForeignKey('parents.id'), primary_key=True) class Bob(Parent): __tablename__ = 'bobs' __mapper_args__ = {'polymorphic_identity': 'bob'} bob_id = Column(Integer, ForeignKey('parents.id'), primary_key=True) 

Ahora me gustaría poder recuperar todos los objetos Tag que tienen una o más relaciones con un objeto Alice . La consulta anterior, session.query(Tag).filter(Tag.parents.any()).all() , no funcionaría ya que no discrimina entre los objetos de Alice o Bob , ni siquiera conoce su existencia. .

Me he metido con Query por un tiempo sin éxito, así que asumo que si se puede hacer, debe tener algo que ver con algunas líneas de código adicionales en las clases de la Tabla, como las que se muestran arriba. Si bien la documentación contiene información sobre clases polimórficas y relaciones de muchos a muchos, y el propio Mike Bayer ofreció a alguien esta respuesta a una pregunta aparentemente relacionada que parece interesante pero que estoy lejos de comprender, estoy un poco atascado.

Las muestras de código pueden disgustar al intérprete de Python, pero espero que se entienda. Dulces para los que me pueden ayudar en mi camino!

Mientras escribía un poco de MWE, encontré una solución, que en realidad es casi la misma que ya había probado. Budder me dio una nueva esperanza para el enfoque, gracias 🙂

 from sqlalchemy import create_engine, ForeignKey, Column, String, Integer, Sequence, Table from sqlalchemy.orm import sessionmaker, relationship, backref from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() association_table = Table('associations', Base.metadata, Column('parent_id', Integer, ForeignKey('parents.id')), Column('tag_id', Integer, ForeignKey('tags.id')), ) class Tag(Base): __tablename__ = 'tags' id = Column(Integer, Sequence('tag_id_seq'), primary_key=True) name = Column(String) class Parent(Base): __tablename__ = 'parents' id = Column(Integer, Sequence('parent_id_seq'), primary_key=True) tags = relationship('Tag', secondary=association_table, backref='parents') class Alice(Parent): __tablename__ = 'alices' __mapper_args__ = {'polymorphic_identity': 'alice'} alice_id = Column(Integer, ForeignKey('parents.id'), primary_key=True) class Bob(Parent): __tablename__ = 'bobs' __mapper_args__ = {'polymorphic_identity': 'bob'} bob_id = Column(Integer, ForeignKey('parents.id'), primary_key=True) engine = create_engine("sqlite://") Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() tag_a = Tag(name='a') tag_b = Tag(name='b') tag_c = Tag(name='c') session.add(tag_a) session.add(tag_b) session.add(tag_c) session.commit() session.add(Alice(tags=[tag_a])) session.add(Bob(tags=[tag_b])) session.commit() for tag in session.query(Tag).\ filter(Tag.parents.any(Parent.id == Alice.alice_id)).\ all(): print(tag.name) 

Si hay un buen enfoque alternativo, todavía estaría interesado. Puedo imaginar a sqlalchemy ofreciendo un enfoque más directo y elegante para que uno pueda hacerlo, por ejemplo:

 for tag in session.query(Tag).\ filter(Tag.alices.any()).\ all(): print(tag.name) 

Si escribes tus clases de objetos de una manera determinada, puedes usar el método de la fuerza contundente … Solo búscalos … Es como un método de consulta simple:

 all_tag_objects = session.query(Tag).all() ## All tag objects in your database tags = [] for tag in all_tag_objects: for parent in tag.parents: if parent.alices != []: ## If there are alice objects in the tag parents alice reltionship instance variable...Then we append the tag because it meets our criteria. flagged_tags.append(tag) 

Parece que encontraste una mejor manera, pero supongo que la prueba definitiva sería hacer una prueba de velocidad.