1. 程式人生 > >Flask 資料庫多對多自引用關係

Flask 資料庫多對多自引用關係

        上篇介紹的多對多關係是兩個模型是之間的多對多關係,關聯表聯接的是兩個明確的實體,還有些情況下只有一個模型,與自己之間存在多對多關係。比如使用者之間的關注。表示使用者關注其他使用者時,只有使用者一個實體,沒有
第二個實體。如果關係中的兩側都在同一個表中, 這種關係稱為自引用關係

       在關注中, 關係的左側是使用者實體,可以稱為“關注者”;關係的右側也是使用者實體,但這些是“被關注者”。從概
念上來看,自引用關係和普通關係沒什麼區別,只是不易理解。下圖 是自引用關係的資料庫圖解,表示使用者之間的關注。


本例的關聯表是 follows,其中每一行都表示一個使用者關注了另一個使用者。圖中左邊表示的一對多關係把使用者和 follows 表中的一組記錄聯絡起來,使用者是關注者。圖中右邊表示的一對多關係把使用者和 follows 表中的一組記錄聯絡起來,使用者是被關注者。

<span style="font-size:18px;">app/models/user.py:關注關聯表的模型實現

class Follow(db.Model):
    __tablename__ = 'follows'
    follower_id = db.Column(db.Integer, db.ForeignKey('users.id'),primary_key=True)

   followed_id = db.Column(db.Integer, db.ForeignKey('users.id'),primary_key=True)

   timestamp = db.Column(db.DateTime, default=datetime.utcnow)</span>


SQLAlchemy 不能直接使用這個關聯表,因為如果這麼做程式就無法訪問其中的自定義欄位。相反地, 要把這個多對多關係的左右兩側拆分成兩個基本的一對多關係,而且要定義成標準的關係。程式碼如下 所示。
<span style="font-size:18px;">app/models/user.py:使用兩個一對多關係實現的多對多關係</span><p><span style="font-size:18px;">
</span></p>
class User(UserMixin, db.Model):
# ...
 followed = db.relationship('Follow',foreign_keys=[Follow.follower_id],
                                      backref=db.backref('follower', lazy='joined'),
                                              lazy='dynamic',
                                              cascade='all, delete-orphan')

 followers = db.relationship('Follow',foreign_keys=[Follow.followed_id],
                                       backref=db.backref('followed', lazy='joined'),
                                               lazy='dynamic',
                                               cascade='all, delete-orphan')

      在這段程式碼中, followed 和 followers 關係都定義為單獨的一對多關係。注意,為了消除外來鍵間的歧義, 定義關係時必須使用可選引數 foreign_keys 指定的外來鍵。而且,db.backref() 引數並不是指定這兩個關係之間的引用關係,而是回引 Follow 模型。回引中的 lazy 引數指定為 joined。這個 lazy 模式可以實現立即從聯結查詢中載入相關對
象。例如,如果某個使用者關注了 100 個使用者,呼叫 user.followed.all() 後會返回一個列表,其中包含 100 個 Follow 例項,每一個例項的 follower 和 followed 回引屬性都指向相應的使用者。設定為 lazy='joined' 模式,就可在一次資料庫查詢中完成這些操作。如果把lazy 設為預設值 select,那麼首次訪問 follower 和 followed 屬性時才會載入對應的使用者,

而且每個屬性都需要一個單獨的查詢, 這就意味著獲取全部被關注使用者時需要增加 100 次額外的資料庫查詢。

        這兩個關係中, User 一側設定的 lazy 引數作用不一樣。 lazy 引數都在“一”這一側設定,返回的結果是“ 多”這一側中的記錄。上述程式碼使用的是 dynamic,因此關係屬性不會直接返回記錄,而是返回查詢物件,所以在執行查詢之前還可以新增額外的過濾器。

        cascade 引數配置在父物件上執行的操作對相關物件的影響。比如,層疊選項可設定為:將使用者新增到資料庫會話後, 要自動把所有關係的物件都新增到會話中。層疊選項的預設值能滿足大多數情況的需求, 但對這個多對多關係來說卻不合用。刪除物件時,預設的層疊行為是把物件聯接的所有相關物件的外來鍵設為空值。 但在關聯表中,刪除記錄後正確的行為應該是把指向該記錄的實體也刪除, 因為這樣能有效銷燬聯接。這就是層疊選項值delete-orphan 的作用。

        cascade 引數的值是一組由逗號分隔的層疊選項,這看起來可能讓人有點困惑,但 all 表示除了 delete-orphan 之外的所有層疊選項。設為 all,delete-orphan 的意思是啟用所有預設層疊選項,而且還要刪除孤兒記錄。