Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write a Hybrid Property that depends on a column in children relationship?

Let's say I have two tables (using SQLAlchemy) for parents and children:

class Child(Base):
     __tablename__ = 'Child'
     id = Column(Integer, primary_key=True) 
     is_boy = Column(Boolean, default=False)
     parent_id = Column(Integer, ForeignKey('Parent.id'))


class Parent(Base):
     __tablename__ = 'Parent'
     id = Column(Integer, primary_key=True) 
     children = relationship("Child", backref="parent")

How can I query a property for whether the parent has any child that is a boy? Hoping to use this column in pandas but not sure how to effectively query it. My intuition is to create a SQLALchemy hybrid property has_a_boy_child, but I am not sure how to define the hybrid property or the matching expression. Thanks!

like image 482
defcal Avatar asked Sep 26 '22 10:09

defcal


1 Answers

Following Correlated Subquery Relationship Hybrid example, I would build a property which returns count of boy children:

@hybrid_property
def has_a_boy_child(self):
    return any(child.is_boy for child in self.children)

@has_a_boy_child.expression
def has_a_boy_child(cls):
    return (
        select([func.count(Child.id)])
        .where(Child.parent_id == cls.id)
        .where(Child.is_boy == True)
        .label("number_of_boy_children")
    )

And you can use it like:

q_has_boys = session.query(Parent).filter(Parent.has_a_boy_child).all()
q_no_boys = session.query(Parent).filter(~Parent.has_a_boy_child).all()
q_attr = session.query(Parent, Parent.has_a_boy_child).all()

Update: If you really would like a bool instead of count (where None would be na in pandas), you can do it as shown below:

@has_a_boy_child.expression
def has_a_boy_child(cls):
    return (
        select([
            case([(exists().where(and_(
                Child.parent_id == cls.id,
                Child.is_boy == True,
            )).correlate(cls), True)],
                else_=False,
            ).label("has_boys")
        ])
        .label("number_of_boy_children")
    )
like image 170
van Avatar answered Sep 30 '22 06:09

van