SQLAlchemy 數據表自關聯


我們說數據表關係時,默認說的是數據表之間的關係「一對多、一對一、多對多等等」。而在實際應用中常常會遇到數據表內的關聯,比如現在互聯中的一個名詞「關注者」和「被關注者」,他們都在用戶範圍內,只是兩個用戶之間的關係。

關係是描述現實世界的實體及其之間各種聯繫的單一的數據結構。

對於使用 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 來打印者兩個語句,語句的內容如下:

  1. 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
  1. 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
`           
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章