我使用SQLAlchemy建模以下概念:
member_of
组members
,并且可以提供access_to
多个Nodes
。accessed_by
多个Groups
我使用关联表模式为User创建多对多关系 - >组和节点 - >基。
代码如下(我使用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
答案 0 :(得分:5)
可以使用连接形成用于获取与给定用户相关的节点的查询:
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()
答案 1 :(得分:1)
我想出了一些有用的东西,但我不确定这是否是最佳方式,因为我不是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))
这是最好的方法吗?