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'
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 的查詢物件。