我們說數據表關係時,默認說的是數據表之間的關係「一對多、一對一、多對多等等」。而在實際應用中常常會遇到數據表內的關聯,比如現在互聯中的一個名詞「關注者」和「被關注者」,他們都在用戶範圍內,只是兩個用戶之間的關係。
關係是描述現實世界的實體及其之間各種聯繫的單一的數據結構。
對於使用 SQLAlchemy 建立數據表之間的關係前面的文章 SQLAlchemy 定義關係 已經進行了介紹,今天主要看單個數據表之內的關聯。
數據表內的一對多關係
數據表自關聯的一對多關係,典型的就是父親和子女的關係。我們通過在表中引用父親的 id 來實現,然後通過反向鏈接來獲取子女的信息。在這裏我們使用 user 和其關注者來實現數據類。
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(100))
followed_id = Column(Integer, ForeignKey("user.id"))
followers = relationship("User", remote_side=[id], backref='user')
在以上示例中,可以通過 follwers 來獲取被關注者的信息,而被關注者可以通過反向鏈接來獲取關注者的信息,在這裏我們假設一個用戶只能關注一個人,一個用戶可以被多個用戶關注。
使用 SQLAlchemy 的完整示例代碼如下:
# -*- coding:utf-8 -*-
from sqlalchemy import (
create_engine,
Column,
Integer,
String,
DateTime,
Text,
ForeignKey,
Table,
MetaData,
)
from sqlalchemy.orm import relationship,backref,sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(100))
followed_id = Column(Integer, ForeignKey("user.id"))
followed = relationship("User", remote_side=[id], backref='user')
engine = create_engine('sqlite:///./test.sqlite')
db_session = sessionmaker(bind=engine)
session = db_session()
Base.metadata.create_all(engine)
user1 = User(name='user1')
user2 = User(name='user2')
user3 = User(name='user3')
session.add(user1)
user2.followed = user1
session.add(user2)
user3.followed = user1
session.add(user3)
session.commit()
user = session.query(User).filter(User.name == 'user2').first()
print(user.name + 'followed:')
print(user.followed.name)
user = session.query(User).filter(User.name == 'user1').first()
print(user.name + 'followers:')
for n in user.user:
print(n.name)
user4 = User(name='user4')
user4.followed = user
session.add(user4)
session.commit()
user = session.query(User).filter(User.name == 'user1').first()
print(user.name + 'followers:')
for n in user.user:
print(n.name)
以上示例的運行結果如下:
user2followers:
user1
user1followed:
user2
user3
user1followed:
user2
user3
user4
數據表內的多對多關係
數據表內自關聯多對多關係的實例那就更多了,比如完整的關注者和被關注者的關係、python 中父類與子類的關係等等。在 SQLAlchemy 中多對多的關係需要藉助於關係表來實現,自關聯多對多的關係也同樣需要關聯表,只是關聯表中關聯的是同一個數據表。
followers = Table('followers', Base.metadata,
Column('follower_id', Integer, ForeignKey('user.id')),
Column('followed_id', Integer, ForeignKey('user.id'))
)
建立關係表後,需要通過 relationship 來建立關係,在兩個數據表的多對多關係中,只需要指定 secondary 參數爲關係表即可,但是在自關聯關係表中的 follower_id 和 follwed_id 指向的是同一個數據表的 id,SQLAlchemy 是無法區分二者的,此時我們需要通過其他的方法來加以區分。
此時需要藉助 relationship 的 primaryjoin 參數和 secondaryjoin 參數。primaryjoin 表達式描述了左表和連接表之間的連接, secondaryjoin 描述了連接表和右表之間的連接.
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(100))
followed = relationship(
'User',
secondary=followers,
# primaryjoin 指明瞭右側對象關聯到左側實體(關注者)的條件
# 也就是根據左側實體查找出對應的右側對象
# 執行 user.followed 時候就是這樣的查找
primaryjoin=(followers.c.follower_id == id),
# secondaryjoin 指明瞭左側對象關聯到右側實體(被關注者)的條件
# 也就是根據右側實體找出左側對象
# 執行 user.followers 時候就是這樣的查找
secondaryjoin=(followers.c.followed_id == id),
# backref 定義了右側實體如何訪問該關係
# 也就是根據右側實體查找對應的左側對象
# 在左側,關係被命名爲 followed
# 在右側使用 followers 來表示所有左側用戶的列表,即粉絲列表
backref=backref('followers', lazy='dynamic'),
lazy='dynamic'
)
在使用中我們可以通過 followed 來獲取關注者列表,通過 followers 來獲取被關注則列表。但是實際上 followed 和 followers 是兩個不同的 SQL 語句,我們可以通過 print 來打印者兩個語句,語句的內容如下:
- user.followed 內容如下
SELECT user.id AS user_id, user.name AS user_name
FROM user, followers
WHERE followers.follower_id = ? AND followers.followed_id = user.id
- user.followers 內容如下
SELECT user.id AS user_id, user.name AS user_name
FROM user, followers
WHERE followers.followed_id = ? AND followers.follower_id = user.id
`