1. 程式人生 > >Flask-SQLAlchemy 中的 relationship & backref

Flask-SQLAlchemy 中的 relationship & backref

 

今天重看 Flask 時,發現對backref仍然沒有理解透徹。查閱文件後發現,以前試圖孤立地理解backref是問題之源,backref是與relationship配合使用的。

一對多關係

db.relationship()用於在兩個表之間建立一對多關係。例如書中 roles 表中一個 User 角色,可以對應 users 表中多個實際的普通使用者。實現這種關係時,要在“多”這一側加入一個外來鍵,指向“一”這一側聯接的記錄。

class Role(db.Model):

# ...

users = db.relationship('User', backref='role')

class User(db.Model):

# ...

role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

relationship & ForeighKey

大多數情況下, db.relationship() 都能自行找到關係中的外來鍵, 但有時卻無法決定把 哪一列作為外來鍵。 例如, 如果 User 模型中有兩個或以上的列定義為 Role 模型的外來鍵, SQLAlchemy 就不知道該使用哪列。如果無法決定外來鍵,你就要為 db.relationship() 提供額外引數,從而確定所用外來鍵。(見書 P49)

relationship & backref

通過db.relationship(),Role 模型有了一個可以獲得對應角色所有使用者的屬性users。預設是列表形式,lazy='dynamic'

時返回的是一個 query 物件。即relationship提供了 Role 對 User 的訪問。

backref正好相反,提供了 User 對 Role 的訪問。

不妨設一個 Role 例項為 user_role,一個 User 例項為 u。relationship 使 user_role.users 可以訪問所有符合角色的使用者,而 backref 使 u.role 可以獲得使用者對應的角色。

示例



$ p manage.py shell

>>> user_role = Role.query.filter_by(name='User').all()

>>> user_role

[<Role u'User'>]

>>> user_role = Role.query.filter_by(name='User').first()

>>> user_role

<Role u'User'>

>>> user_role.users

<sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x1087c1050>

>>> user_role.users.order_by(User.username).all()

[<User u'alice78'>, <User u'andrea86'>, <User u'hmr'>]

>>> Role.query.all()

[<Role u'Moderator'>, <Role u'Administrator'>, <Role u'User'>]

>>> user_role.users.count()

3

>>> u = User.query.filter_by(username='hmr').first()

>>> u

<User u'hmr'>

>>> u.role

<Role u'User'>

 

一對一關係

除了一對多之外, 還有幾種其他的關係型別。一對一關係可以用前面介紹的一對多關係表示, 但呼叫 db.relationship() 時要把 uselist 設為 False , 把“多”變成“一”。

多對多關係

如果你想要用多對多關係,你需要定義一個用於關係的輔助表。對於這個輔助表, 強烈建議  使用模型,而是採用一個實際的表:

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
    db.Column('page_id', db.Integer, db.ForeignKey('page.id'))
)

class Page(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', secondary=tags,
        backref=db.backref('pages', lazy='dynamic'))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)

這裡我們配置 Page.tags 載入後作為標籤的列表,因為我們並不期望每頁出現 太多的標籤。而每個 tag 的頁面列表( Tag.pages )是一個動態的反向引用。 正如上面提到的,這意味著你會得到一個可以發起 select 的查詢物件。