簡體   English   中英

SQLAlchemy 多對多關聯查詢特定子項

[英]SQLAlchemy many-to-many association querying specific child

在多對多關系的情況下,可以以關聯對象模式的形式使用關聯表。

我通過UserCouncil關聯表對兩個具有 M2M 關系的類進行了以下設置。

class Users(Base):
    name = Column(String, nullable=False)
    email = Column(String, nullable=False, unique=True)
    created_at = Column(DateTime, default=datetime.utcnow)
    password = Column(String, nullable=False)
    salt = Column(String, nullable=False)
    
    councils = relationship('UserCouncil', back_populates='user')


class Councils(Base):
    name = Column(String, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)

    users = relationship('UserCouncil', back_populates='council')


class UserCouncil(Base):
    user_id = Column(UUIDType, ForeignKey(Users.id, ondelete='CASCADE'), primary_key=True)
    council_id = Column(UUIDType, ForeignKey(Councils.id, ondelete='CASCADE'), primary_key=True)

    role = Column(Integer, nullable=False)

    user = relationship('Users', back_populates='councils')
    council = relationship('Councils', back_populates='users')

但是,在這種情況下,假設我想為給定用戶user1搜索具有特定名稱cname的委員會。 我可以執行以下操作:

for council in user1.councils:
    if council.name == cname:
        dosomething(council)

或者,或者,這個:

session.query(UserCouncil) \
       .join(Councils)     \
       .filter((UserCouncil.user_id == user1.id) & (Councils.name == cname)) \
       .first()            \
       .council

雖然第二個更類似於原始 SQL 查詢並且性能更好,但第一個更簡單。 有沒有其他更慣用的方式來表達這個查詢,這種方式在利用關系鏈接而不是顯式編寫傳統連接的同時性能更好?

首先,我認為即使您作為示例帶來的 SQL 查詢也可能需要再次將UserCouncil.council關系獲取到DB ,如果它尚未加載到內存中。

我認為,鑒於您想根據其.nameUser實例直接搜索Council實例,這正是您應該要求的。 以下是關於如何過濾user_id的 2 個選項的查詢(您可能更熟悉第二個選項,因此請使用它):

q = (
    select(Councils)
    .filter(Councils.name == councils_name)
    .filter(Councils.users.any(UserCouncil.user_id == user_id))  # v1: this does not require JOIN, but produces the same result as below
    # .join(UserCouncil).filter(UserCouncil.user_id == user_id)    # v2: join, very similar to original SQL
)
council = session.execute(q).scalars().first()

至於讓它更簡單和慣用,我只能建議將它包裝在User實例上的方法或屬性中:

class Users(...):
    ...
    def get_council_by_name(self, councils_name):
        q = (
            select(Councils)
            .filter(Councils.name == councils_name)
            .join(UserCouncil).filter(with_parent(self, Users.councils))
        )
        return object_session(self).execute(q).scalars().first()

這樣您以后就可以將其稱為user.get_council_by_name('xxx')


Edit-1:添加了 SQL 查詢

上面第一個q查詢的 v1 將生成以下 SQL:

SELECT  councils.id,
        councils.name
FROM    councils
WHERE   councils.name = :name_1
  AND  (EXISTS
         (SELECT  1
          FROM    user_councils
          WHERE   councils.id = user_councils.council_id
            AND   user_councils.user_id = :user_id_1
         )
       )

而 v2 選項將生成:

SELECT  councils.id,
        councils.name
FROM    councils
JOIN    user_councils ON councils.id = user_councils.council_id
WHERE   councils.name = :name_1
  AND   user_councils.user_id = :user_id_1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM