[英]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
,如果它尚未加載到內存中。
我認為,鑒於您想根據其.name
和User
實例直接搜索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.