search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

Flask 進階系列:SQLAlchemy 擴展學習

熟悉 Java 的朋友們一定使用過 Hibernate 或 MyBatis 吧,這類的框架稱為對象關係映射 ORM 框架,它將對資料庫的操作從繁瑣的 SQL 語言執行簡化為對象的操作。Python 中也有類似的 ORM 框架,叫 SQLAlchemy。本篇我們將介紹 Flask 中支持 SQLAlchemy 框架的第三方擴展,Flask-SQLAlchemy。

安裝和啟用

在閱讀此文之前,強烈建議讀者先了解 SQLAlchemy 的基本知識。

我們依然通過 pip 安裝:

  • $ pip install Flask-SQLAlchemy

PyPI 自動會將其所依賴的 SQLAlchemy 包裝上。我們可以採用下面的方法初始化一個 Flask-SQLAlchemy 的實例:

  • from flask importFlask

  • from flask.ext.sqlalchemy importSQLAlchemy

  • app =Flask(__name__)

  • app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///db/users.db'

  • db =SQLAlchemy(app)

應用配置項"SQLALCHEMYDATABASEURI"指定了 SQLAlchemy 所要操作的資料庫的連接字元串,本文中我們使用 SQLite3,連接字元串以"sqlite:///"開頭,後面的"db/users.db"表示資料庫文件是當前位置下 db 子目錄中的"users.db"文件。

定義模型

一個模型即對應資料庫中的一個表,這裡我們來定義一個用戶模型:

  • classUser(db.Model):

  • id = db.Column(db.Integer primary_key=True)

  • name = db.Column(db.String(50), unique=True)

  • age = db.Column(db.Integer)

  • def __init__(self name age):

  • self.name = name

  • self.age = age

  • def __repr__(self):

  • return''%self.name

模型類必須繼承"db.Model", db 即上一節的"db = SQLAlchemy(app)",上例中的 User 模型將自動映射到資料庫中的"user"表。User 模型中定義了三個屬性:

  • "id":整型主鍵

  • "name":最大長度為 50 的字元串,且值唯一

  • "age":整型

這三個屬性將分別對應"user"表中"id"主鍵, "name"和"age"欄位。寫好"init"和"repr" 方法,我們的模型就定義完成了。現在你就可以通過下面的代碼來創建資料庫和表:

  • db.create_all

讓我們來驗證下,"user"表是否創建成功。首先打開資料庫文件:

  • $ sqlite3 db/users.db

查詢下"user"表的 schema:

  • sqlite.schema user

你應該可以看到下面的信息:

  • CREATE TABLE user (

  • id INTEGER NOT NULL

  • name VARCHAR(50),

  • age INTEGER

  • PRIMARY KEY (id),

  • UNIQUE (name)

  • );

另外,你可以通過"db.drop_all"方法刪除所有的表,不過資料庫文件將會被保留。

添加數據

數據表創建完后,讓我們添加些數據進去:

  • db.session.add(User('Michael'18))

  • db.session.add(User('Tom'21))

  • db.session.add(User('Jane'17))

  • db.session.commit

一定要記得調用"db.session.commit"提交事務,不然數據不會保存到資料庫中。我們無需指定每條記錄的"id"主鍵值,資料庫會自動使用自增的數值作為主鍵。

查詢數據

每個數據模型都有"query"介面可以用來查詢模型所對應的表的記錄。比如,查詢"user"表中的所有記錄:

  • users =User.query.all

返回的 users 是一個列表,其中每個元素都是一個 User 類型的對象,對應於"user"表中的一條記錄。該方法相當於執行了 SQL 語句:

  • SELECT * FROM user

"query"介面擁有豐富的方法,這裡列舉一些常用的:

  • "filter_by"方法,對查詢結果過濾,參數必須是鍵值對"key=value"

  • # WHERE name='Tom'

  • users =User.query.filter_by(name='Tom')

  • # WHERE name='Tom' AND age=17

  • users =User.query.filter_by(name='Jane' age=17)

效果相當於使用了 WHERE 子句,多個鍵值對用逗號分割。

  • "filter"方法,對查詢結果過濾,比"filter_by"方法更強大,參數是布爾表達式

  • # WHERE age<20

  • users =User.query.filter(User.age<20)

  • # WHERE name LIKE 'J%' AND age<20

  • users =User.query.filter(User.name.startswith('J'),User.age<20)

多個查詢條件用逗號分割。

  • "first"方法,取返回列表中的第一個元素,當我們只查詢一條記錄時非常有用

  • user =User.query.filter_by(name='Michael').first

  • "order_by"方法,排序

  • from sqlalchemy import desc

  • # ORDER BY name

  • user =User.query.order_by(User.name)

  • # ORDER BY age DESC, name

  • user =User.query.order_by(desc(User.age),User.name)

  • "limit"和"offset"方法,分頁

  • # LIMIT 10 OFFSET 10

  • user =User.query.limit(10).offset(10)

等同於 MySQL 中的 LIMIT 和 OFFSET,上例中我們從第 11 條記錄開始取,並最多只取 10 條。

  • "slice(start, stop)",分頁

  • # LIMIT 2 OFFSET 1

  • user =User.query.slice(13)

從 start 位置開始取記錄,到 stop 位置前結束。本質上來說,SQLAlchemy 會將其翻譯成 LIMIT/OFFSET 語句來實現,上例中的"slice(1, 3)"等同於"LIMIT 2 OFFSET 1″。

更新數據

在添加數據時,我們使用了"add"方法,其實它一樣可以用來更新數據:

  • user =User.query.filter_by(name='Tom').first

  • if user isnotNone:

  • user.age =1

  • db.session.add(user)

  • db.session.commit

SQLAlchemy 會自動判斷,如果對象對應的記錄已存在,就更新而不是添加。

SQLAlchemy 還支持批量更新,比如我們要將所有歲數小於 20 的人都加 1 歲:

  • User.query.filter(User.age<20).update({'age':User.age 1})

  • db.session.commit

更新完后,別忘了提交事務。

刪除數據

只需調用"delete"方法即可,傳入的參數是對應資料庫中記錄的對象。記得同"add"一樣,要調用"commit"來提交事務:

  • user =User.query.filter_by(name='Michael').first

  • if user isnotNone:

  • db.session.delete(user)

  • db.session.commit

一對多關係

現在讓我們再添加一個模型,成績單。每個用戶對於不同的課程,會有不同的分數,這樣用戶同成績單之前就是一對多的關係。怎麼在模型類的定義中體現這個一對多關係呢。保持 User 類不變,現在讓我們添加一個 Score 類:

  • from datetime import datetime

  • classScore(db.Model):

  • id = db.Column(db.Integer primary_key=True)

  • course = db.Column(db.String(50))

  • assess_date = db.Column(db.DateTime)

  • score = db.Column(db.Float)

  • is_pass = db.Column(db.Boolean)

  • user_id = db.Column(db.Integer db.ForeignKey('user.id'))

  • user = db.relationship('User' backref=db.backref('scores' lazy='dynamic'))

  • def __init__(self course score user assess_date=None):

  • self.course = course

  • self.score = score

  • self.is_pass =(score >=60)

  • if assess_date isNone:

  • assess_date = datetime.now

  • self.assess_date = assess_date

  • self.user = user

  • def __repr__(self):

  • return''%(self.courseself.user.name)

Score 模型中有這些屬性:

  • "id":整型主鍵

  • "course":最大長度為 50 的字元串

  • "assess_date":日期時間類型

  • "score":浮點型

  • "is_pass":布爾型

分別對應資料庫"score"表中"id"主鍵, "course", "accessdate", "score"和"ispass"欄位。另外,它還有兩個屬性:

  • "user_id":整型外鍵,對應於"user"表的主鍵"id"

  • "user":User 對象

"user_id"欄位聲明了外鍵,也就相當於聲明了"user"表同"score"表的一對多關係。"user"屬性並不是數據表中的欄位,它使用了"db.relationship"方法,使得我們可以通過"Score.user"訪問當前 score 記錄的 user 對象,它的第一個參數"User"就表明了對應的對象模型是 User。而第二個參數"backref"定義了從 User 模型反向引用 Score 模型的方法,上例中,我們就可以用"User.scores"獲取當前 user 對象所有的 score 記錄,它是一個列表。"db.backref"方法的"lazy"參數決定了在 User 對象中什麼時候載入其 scores 列表的值,延遲載入可以提高性能,並避免內存的浪費,"lazy"參數的選擇可以 參閱這裡。

現在查詢下"score"表的 schema,你會看到下面的結果:

  • CREATE TABLE score (

  • id INTEGER NOT NULL

  • course VARCHAR(50),

  • assess_date DATETIME

  • score FLOAT

  • is_pass BOOLEAN

  • user_id INTEGER

  • PRIMARY KEY (id),

  • CHECK (is_pass IN (01)),

  • FOREIGN KEY(user_id) REFERENCES user (id)

  • );

讓我們添加些 score 記錄:

  • user =User.query.filter_by(name='Tom').first

  • if user isnotNone:

  • db.session.add(Score('Math'80.5 user))

  • db.session.add(Score('Politics'58 user))

  • user =User.query.filter_by(name='Jane').first

  • if user isnotNone:

  • db.session.add(Score('Math'88 user))

  • db.session.commit

然後試試通過"User.scores"查詢某個用戶的成績:

  • def scores(name):

  • user =User.query.filter_by(name=name).first

  • if user isnotNone:

  • for score in user.scores:

  • print'Name "%s" course "%s", score is %s'%(name score.course score.score)

對於多對多關係,大家可以創建一個單獨的關係表,然後每個表同這個關係表都是一對多的關係。或者大家可以參考 官方文檔上的例子來實現多對多關係。

更多參考資料

SQLAlchemy 的官方文檔

Flask-SQLAlchemy 的官方文檔

Flask-SQLAlchemy 的源碼

題圖:pexels,CC0 授權。



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦