繁体   English   中英

跨多个SQLAlchemy多对多查询

[英]Querying across multiple SQLAlchemy many-to-many relationships

我正在使用SQLAlchemy为以下概念建模:

  • 用户可以是一个member_of
  • 组可以具有多个用户作为members并且可以提供对多个Nodes access_to
  • 一个节点可以被多个Groups accessed_by

我正在使用关联表模式为用户->组和节点->组创建多对多关系。

下面的代码(我正在使用Flask-SQLAlchemy,这就是为什么我有db.Model等的原因)

如何查询用户所属的所有组均可访问的所有节点?

这是对象模型

group_access = Table('access', db.metadata,
                     Column('group_id', Integer, ForeignKey('groups.group_id')),
                     Column('nid', BigInteger, ForeignKey('nodes.nid')),
                     )

group_membership = Table('membership', db.metadata,
                         Column('group_id', Integer, ForeignKey('groups.group_id')),
                         Column('uid', Integer, ForeignKey('users.id')),
                         )

edges = Table('edges', db.metadata,
              Column('src_id', BigInteger, ForeignKey('nodes.nid'), primary_key=True),
              Column('dest', BigInteger, ForeignKey('nodes.nid'), primary_key=True),
              )


class User(db.Model):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    email = Column(String, unique=True, nullable=False)
    member_of = relationship("Group", secondary=group_membership, back_populates='members')

    def __init__(self, email):
        self.email = email


class Node(db.Model):
    __tablename__ = 'nodes'

    nid = Column(BigInteger, primary_key=True)
    type = Column(String)
    parents = relationship("Node",
                           secondary=edges,
                           primaryjoin="Node.nid==edges.c.src_id",
                           secondaryjoin="Node.nid==edges.c.dest",
                           backref="children")
    accessed_by = relationship("Group", secondary=group_access, back_populates='access_to')

    def __init__(self, owner=None, type=None):
        self.type = type
        # self.owner = owner

    def __repr__(self):
        return "<{} nid:{} >".format(self.type, self.nid)


class Group(db.Model):
    __tablename__ = 'groups'

    group_id = Column(Integer, primary_key=True)
    type = Column(String)
    members = relationship("User", secondary=group_membership, back_populates="member_of")
    access_to = relationship("Node", secondary=group_access, back_populates="accessed_by")

    def __init__(self, name):
        self.type = name

    def __repr__(self):
        return self.type

可以使用联接来形成用于获取与给定用户相关的节点的查询:

db.session.query(Node).\
    join(Node.accessed_by).\
    join(Group.members).\
    filter(User.id == u.id).\
    all()

由于SQLAlchemy如何处理模型实体,所以重复行不是问题。 您还可以使用EXISTS,它不会产生重复项:

db.session.query(Node).\
    filter(Node.accessed_by.any(
        Group.members.any(User.id == u.id))).\
    all()

我想出了一些可行的方法,但是由于我不是SQLAlchemy或数据库专家,因此不确定这是否是最好的方法。 另外,查询不直接使用关系,我认为可能会有更好的方法。

def find_nodes_for_user(u):
    users_groups = db.session.query(group_membership.c.group_id) \
                             .filter(group_membership.c.uid == u.id) \
                             .subquery()
    node_id = db.session.query(group_access.c.nid) \
                        .filter(group_access.c.group_id.in_(users_groups)) \
                        .subquery()
    return db.session.query(Node).filter(Node.nid.in_(node_id)).all()

这将产生以下SQL:

SELECT nodes.nid AS nodes_nid, nodes.type AS nodes_type 
FROM nodes 
WHERE nodes.nid IN (SELECT access.nid 
FROM access 
WHERE access.group_id IN (SELECT membership.group_id 
FROM membership 
WHERE membership.uid = %(uid_1)s))

这是最好的方法吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM