2017-09-14 160 views
0

我使用SQLAlchemy的模型如下概念:查詢SQLAlchemy的許多一對多的關係

  • 用戶可以是一個member_of
  • A組可以有多個用戶爲members和可以提供access_to多個Nodes
  • 節點可以是accessed_by多個Groups

我使用的關聯表模式創造出許多爲用戶帶來很多的關係 - >集團及節點 - >集團。

的代碼如下(我用的燒瓶的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

您希望如何查詢節點?作爲一個單獨的查詢,或者您想在提取用戶時重新加載它們嗎? –

+0

讓我們假設一個單獨的查詢,讓我們假設給定用戶可以訪問的節點本身可能沒有父子關係。所以數據將是一棵樹,用戶可以訪問其中的不同節點。 – markand

回答

1

查詢來獲取與給定用戶可以利用形成節點連接:

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() 
+0

哇,謝謝!我喜歡這兩個比我想出來的更好。每個人都做我所堅持的 - 穿越關係。 – markand

+0

編寫這樣的「遍歷」的方法更短,但起初可能看起來很混亂。在單個實體查詢中,join()也接受字符串作爲關係屬性的名稱來加入,所以2個單獨的聯接可以寫成join(「accessible_by」,「members」)。 –

+0

Ilja,你是不斷奉獻的禮物! :-) – markand

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)) 

這是做的最好的方法是什麼?

+0

完美無瑕,儘管您可以用較少的連接表示相同的內容,或者使用EXISTS。 –

+0

謝謝!你能舉一個你在想什麼的例子嗎? – markand

相關問題