book
flask
flask 2.x
flask extension
sqlalchemy
python
Flask-SQLAlchemy
SQLAlchemy
Relationship Configuration
關聯式資料庫中最重要的就是關聯的設置,當我們把資料庫當做物件之後應該如何來設置,就是這一邊要說明的部份。比較有趣的是,改版之後的說明文件並沒有看到關聯設置的範例,所以這部份不是看舊版文件就是要看SQLAlchemy,個人是建議看SQLAlchemy的文件就是了。
關聯式資料庫的關聯方式有很多種,一對一、一對多、多對多,我們就把SQLAlchemy的範例調整成用Flask-SQLAlchemy來做說明:
class Parent(db.Model):
__tablename__ = "parent_table"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
children = db.relationship("Child")
class Child(db.Model):
__tablename__ = "child_table"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
parent_id = db.Column(db.Integer, db.ForeignKey("parent_table.id"))
db.relationship
來做關聯設置,參數中放的是關聯目標的類別名稱,案例中為Child
db.ForeignKey
,參數中放的是目標資料表中的鍵值,案例中為parent_table.id
上面是根據SQLAlchemy的範例調整過來的,不難發現它的關聯邏輯就是父表對子表,屬一對多,主要利用db.relationship
來做關聯的設定。
事實上,如果你想設置一對一的關聯的話,只需要調整為db.relationship("Child", uselist=False)
即可。
下面我們來做幾個操作,快速瞭解orm帶給我們的一些便利:
pp = Parent(name='A')
pp.children
[]
因為我們的資料表是新建的,而且裡面都沒有任何資料,所以當我們去看pp.children
的時候會得到一個空的list
cc = Child(name='A_C')
pp.children.append(cc)
with app.app_context():
db.session.add(pp)
db.session.commit()
Partent
這邊我們把產生的Child
做為一個物件寫入Parent
設置的關聯屬性,也就是上面我們說的那個空的list
,然後完成這筆交易記錄,記得,flask-sqlalchemy的交易記錄只能寫在flask的context之中。然後回頭看一下我們的資料表:
可以先看一下,基本上parent_table
有資料不稀奇,這很正常,不過如果你去看child_table
的話就會發現:
經過我們剛剛在Parent
把Child
給append
進去那個關聯設置之後,一次性的add
可以在兩張表同時生成資料,而且連關聯用的鍵值也同時記錄了。這就是orm給我們的一個便利性。
上面的設置是一對多,反過來就是多對一,互相認識一下,這只需要做一個小調整:
class Parent(db.Model):
__tablename__ = "parent_table"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
children = db.relationship("Child", back_populates='parent')
class Child(db.Model):
__tablename__ = "child_table"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
parent_id = db.Column(db.Integer, db.ForeignKey("parent_table.id"))
parent = db.relationship('Parent', back_populates='children')
上面程式碼可以看的出來關聯設置上的差異,利用back_populates.back_populates
來做為雙邊關聯的設置。
官方文件中有提到,backref
是屬於遺留下來的接口,建議還是用上面的設置方式較為直觀。
一樣的,你可以利用select Child
的方式來確認是不是能取得它的相對應的Parent
:
stms = db.select(Child)
results = db.session.execute(stms)
result = results.fetchone()[0]
result.parent.name
>>> 'A'
result.name
>>> 'A_C'
這邊可能要稍微注意一下,使用fetchone
與scalar
得到的回傳是不一樣的,這部份可以參考官方文件說明。最主要是fetchone
、fetchall
回傳的是sqlalchemy的row;而scalar
、scalars
則是回傳實例,親手做一次就會有感覺。
大致上我們已經知道如何設置一對一、一對多、多對一的關聯,當然還有多對多的關聯,不過這部份不急,後續實作過程中如果有緣就會使用到,一篇文章如果有太多資訊就會造成消化不良以及閱讀障礙。
實務上在資料庫的操作中還會有刪除、更新的需求,刪除的話我們可能會希望當Child
沒有資料的時候就刪除相對應的Parent
,這一樣的,後續實作中我們就會明白如何處理。