SQLAlchemy 链式关联代理用于曾孙吗?

9 投票
1 回答
2870 浏览
提问于 2025-04-30 17:08

我有四个类,分别是:Group(组)、Parent(父母)、Child(孩子)和Toy(玩具)。

  • Group(组)有一个指向Parent(父母)的关系。
  • Parent(父母)有一个指向Child(孩子)的关系。
  • Child(孩子)有一个指向Toy(玩具)的关系。

Parent(父母)有一个叫做toysassociation_proxy,可以获取所有Toy(玩具),这些玩具是Parent(父母)的孩子们拥有的。

我想要获取一个Group(组)中的所有玩具。我尝试在Group(组)上创建一个指向Parent(父母)玩具的association_proxy,但结果是这样的:

[[<Toy 1>, <Toy 2>], [], [], []]

而我想要的是:

[<Toy 1>, <Toy 2>]

如果Parent(父母)的children(孩子)没有任何Toy(玩具),那么toys的关联代理就会是[](空列表)。但是,第二个关联代理并不知道要排除这些空列表。而且,这些列表应该被合并。有没有办法让这个工作正常?

class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime)
    name = db.Column(db.String(80, convert_unicode=True))
    # relationships
    parents = db.relationship('Parent', backref='group')
    toys = association_proxy('parents', 'toys')

class Parent(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    group_id = db.Column(db.Integer, db.ForeignKey('group.id', ondelete='CASCADE'))
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime)
    first_name = db.Column(db.String(80, convert_unicode=True))
    last_name = db.Column(db.String(80, convert_unicode=True))
    children = db.relationship('Child', backref='parent', cascade='all, delete')
    toys = association_proxy('children', 'toys')

class Child(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime)

class Toy(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    child_id = db.Column(db.Integer, db.ForeignKey('child.id', ondelete='CASCADE'))
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime)
    child = db.relationship('Child', backref=db.backref("toys",cascade="all, delete-orphan", order_by="desc(Toy.id)"))
暂无标签

1 个回答

9

既然这些只是用来获取和查看数据(就像你在评论中提到的,添加数据会让事情变得模糊),我更倾向于使用一个 viewonly 的关系,而不使用 association_proxy

class Group(db.Model):
    # ...
    toys = relationship('Toy',
        secondary="join(Group, Parent, Group.id == Parent.group_id).join(Child, Parent.id == Child.parent_id)",
        primaryjoin="and_(Group.id == Parent.group_id, Parent.id == Child.parent_id)",
        secondaryjoin="Child.id == Toy.child_id",
        viewonly=True,
    )

请注意,这是 SQLAlchemy 的一个新特性,详细信息可以在文档的 复合“次级”连接 部分找到。

然后你可以仅仅用它来查询数据:

group_id = 123
group = session.query(Group).get(group_id)
print(group.toys)

或者你甚至可以用它来过滤数据,比如要找一个名字叫“超级马里奥”的玩具所在的组,你可以这样做:

group = session.query(Group).filter(Group.toys.any(Toy.name == "Super Mario"))

但实际上,你可以用简单的查询来完成所有这些,或者创建一个支持查询的属性。有关更多信息,请查看文档中的 自定义列属性 部分,在那里你可以使用任何简单的属性,column_propertyhybrid attribute

撰写回答