# Flask Extension(03)\_Flask-SQLAlchemy(3) ###### tags: `book` `flask` `flask 2.x` `flask extension` `sqlalchemy` `python` [Flask-SQLAlchemy](https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/) [SQLAlchemy](https://docs.sqlalchemy.org/) [Relationship Configuration](https://docs.sqlalchemy.org/en/14/orm/relationships.html) 關聯式資料庫中最重要的就是關聯的設置,當我們把資料庫當做物件之後應該如何來設置,就是這一邊要說明的部份。比較有趣的是,改版之後的說明文件並沒有看到關聯設置的範例,所以這部份不是看舊版文件就是要看[SQLAlchemy](https://docs.sqlalchemy.org/),個人是建議看SQLAlchemy的文件就是了。 ## 關聯 關聯式資料庫的關聯方式有很多種,一對一、一對多、多對多,我們就把SQLAlchemy的[範例](https://docs.sqlalchemy.org/en/14/orm/relationships.html)調整成用Flask-SQLAlchemy來做說明: ```python= 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")) ``` * 5:利用`db.relationship`來做關聯設置,參數中放的是關聯目標的類別名稱,案例中為`Child` * 12:關聯設置需同時設置外來鍵,也就是`db.ForeignKey`,參數中放的是目標資料表中的鍵值,案例中為`parent_table.id` 上面是根據SQLAlchemy的範例調整過來的,不難發現它的關聯邏輯就是父表對子表,屬一對多,主要利用`db.relationship`來做關聯的設定。 :::info 事實上,如果你想設置一對一的關聯的話,只需要調整為`db.relationship("Child", uselist=False)`即可。 ::: 下面我們來做幾個操作,快速瞭解orm帶給我們的一些便利: ```python= pp = Parent(name='A') pp.children [] ``` * 1:新增一筆記錄 * 2:觀察我們設置的關聯是否正確 因為我們的資料表是新建的,而且裡面都沒有任何資料,所以當我們去看`pp.children`的時候會得到一個空的`list` ```python= cc = Child(name='A_C') pp.children.append(cc) with app.app_context(): db.session.add(pp) db.session.commit() ``` * 1:新增一筆記錄 * 2:將新增的資料寫入`Partent` * 3:資料寫入資料表,並完成交易記錄 這邊我們把產生的`Child`做為一個物件寫入`Parent`設置的關聯屬性,也就是上面我們說的那個空的`list`,然後完成這筆交易記錄,記得,flask-sqlalchemy的交易記錄只能寫在flask的context之中。然後回頭看一下我們的資料表:  可以先看一下,基本上`parent_table`有資料不稀奇,這很正常,不過如果你去看`child_table`的話就會發現:  經過我們剛剛在`Parent`把`Child`給`append`進去那個關聯設置之後,一次性的`add`可以在兩張表同時生成資料,而且連關聯用的鍵值也同時記錄了。這就是orm給我們的一個便利性。 上面的設置是一對多,反過來就是多對一,互相認識一下,這只需要做一個小調整: ```python= 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`來做為雙邊關聯的設置。 :::info 官方文件中有提到,`backref`是屬於遺留下來的接口,建議還是用上面的設置方式較為直觀。 ::: 一樣的,你可以利用`select Child`的方式來確認是不是能取得它的相對應的`Parent`: ```python= stms = db.select(Child) results = db.session.execute(stms) result = results.fetchone()[0] result.parent.name >>> 'A' result.name >>> 'A_C' ``` :::info 這邊可能要稍微注意一下,使用`fetchone`與`scalar`得到的回傳是不一樣的,這部份可以參考官方[文件說明](https://docs.sqlalchemy.org/en/14/orm/queryguide.html#selecting-orm-entities-and-attributes)。最主要是`fetchone`、`fetchall`回傳的是sqlalchemy的row;而`scalar`、`scalars`則是回傳實例,親手做一次就會有感覺。 ::: ## 結論 大致上我們已經知道如何設置一對一、一對多、多對一的關聯,當然還有多對多的關聯,不過這部份不急,後續實作過程中如果有緣就會使用到,一篇文章如果有太多資訊就會造成消化不良以及閱讀障礙。 實務上在資料庫的操作中還會有刪除、更新的需求,刪除的話我們可能會希望當`Child`沒有資料的時候就刪除相對應的`Parent`,這一樣的,後續實作中我們就會明白如何處理。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up