首先,安裝flask-sqlalchemy擴充:
$pip install flask-sqlalchemy
然後,在項目中導入SQLAlchemy類,并執行個體化應用程式使用的資料庫(以mysql為例):
db對象是SQLAlchemy類的執行個體,表示程式使用的資料庫,同時還獲得了Flask-SQLAlchemy提供的所有功能。
在Python中,可以使用資料庫相應的包直接操作資料庫,如PyMySQL操作MySQL資料庫,還有一些資料庫抽象層代碼包供選擇,如這裡要讨論的SQLAlchemy。該抽象包直接處理高等級的Python對象,而不用處理如表這樣的資料庫實體。
抽象層,就是所謂的對象關系映射(ORM),其最大的優勢就是:能在使用者不知覺的情況下把高層的面向對象操作轉換成低層的資料庫指令,極大簡化代碼編寫。SQLAlchemy就是已與Flask很好內建的更高層抽象例子,其還支援多種關系型資料庫引擎。
基于SQLAlchemy的模型定義:
tablename定義在資料庫中使用的表名;
db.Column類構造函數的第1個參數是資料庫表列(也是模型屬性)的類型,其餘的參數指定屬性(資料庫表列)的配置選項。
2.1 常用的SQLAlchemy列類型
類型名
Python類型
說明
Integer
int
普通整數,一般32位
SmallInteger
取值範圍小的整數,一般16位
BigInteger
int或long
不限制精度的整數
Float
float
浮點數
String
str
變長字元串
Boolean
bool
布爾值
Date
datetime.date
日期
Time
datetime.time
時間
DateTime
datetime.datetime
日期和時間
Text
變長字元串,對較長或不限長度的字元串做了優化
Numeric
decimal.Decimal
定點小數
2.2 常用的SQLAlchemy列選項
選項名
primary_key
如果設為True,這列就是表的主鍵
unique
如果設為True,這列不允許重複值
index
如果設為True,為該列建立索引,提升查詢效率
nullable
如果設為True,這列允許null值,如果設為false,不允許為空
default
為這列定義預設值
關系型資料庫使用關系把不同表中的行聯系起來。以上述模型定義代碼為例,假設角色對使用者是一對多的關系(即1個角色可屬于多個使用者,而每個使用者隻能有1個角色)。
在“多”的一方,使用外鍵定義關系
如在Users模型中,定義role_id列為外鍵,給db.Column構造函數傳遞db.ForeignKey()參數,并将roles.id作為db.ForeignKey()的參數,其表明role_id列的值是roles表中行的id值。
在“一”的一方,基于面向對象的視角建立代表執行個體(記錄或行)的屬性,如上述的users = db.relationship('Users', backref='role')
db.relationship()的第1個參數表明這個關系的另一端是哪個模型,backref參數向Users模型添加一個role屬性,進而定義反向關系。通過這一屬性(role)可以替代role_id通路Roles模型,此時将擷取的是模型對象,而不是外鍵的值。
如何了解上述這段話,可以從下面2句代碼加深:
通過第1句代碼,可以直接獲得特定角色執行個體(inst_role)相對應的所有users對象,且是以清單形式傳回。
通過第2句代碼,可以通過user執行個體直接獲得該user所對應的role對象(1行記錄,而不是Users模式定義的role_id字段值)。
要定義一對一的關系,隻需基于一對多的模型定義基礎上,給db.relationship()函數多傳一個關鍵字表示關系選項:
users = db.relationship('Users', backref='role', userlist=False)
一對多與一對一在編碼時,有個點需特别注意:當通過“一”的執行個體(db.relationship定義方)擷取多的一方的對象時:
一對多:users = inst_role.users傳回的是對象清單
一對一:users = inst_role.users傳回的就是對象,而非清單
Pending...
在Flask-SQLAlchemy中,對資料庫所做的改動均是通過資料庫“會話”進行管理的,會話用db.session表示。如需用db.session.commit()送出對記錄的修改,始終把資料庫相關改動放在會話中送出,可避免因部分更新異常導緻資料庫中資料的不一緻性。
前4行代碼,執行個體化2種角色及2個使用者對象(映射至資料庫即是給記錄的字段指派)
5~7行代碼就是将新增角色及使用者操作放在了1個session中,最後再統一送出(commit),可防止因其中某條語句異常而更新部分進而導緻資料的不一緻性。該操作就是将多個原子操作組成一個事務,如果某條更新失敗就會導緻整個會話失效。
5~7行還可簡寫成: db.session.add([admin_role, mod_role, john, david])
在Flask-SQLAlchemy中,一條記錄表示為一個對象;記錄的字段表示為對象的屬性,是以要更新字段值,實際上就是對對象的屬性指派:
查詢表中所有記錄:模式.query.all()
eg. Roles.query.all()
使用過濾器進行更精确查詢
過濾器
filter
把過濾器添加到原查詢上
filter_by
把等值過濾器添加到原查詢上
limit
使用指定的值限制原查詢傳回的結果數量
offset
偏移原查詢傳回的結果
order_by
根據指定條件對原查詢結果進行排序
group_by
根據指定條件對原查詢結果進行分組
2.1 filter與filter_by差別
2.1.1 文法差別
filter需要用“類名.屬性名”且需用==比較,而filter_by直接用屬性名,比較用=
2.1.2 組合查詢
filter不支援組合查詢,隻能連續用filter來實作,而filter_by支援組合查詢(下面2條語句效果一樣)
注:如果要檢視SQLAlchemy為查詢生成的原生SQL查詢語句,隻需把query對象轉換成字元串: str(Users.query.filter_by(role=admin_role))
執行查詢
在查詢上應用指定的過濾器後,通過調用all()觸發執行查詢,常見的觸發執行方法有:
方法
all()
查詢所有結果
first()
傳回查詢的第1個結果,沒有傳回None
get()
傳回指定主鍵對應的行,沒有傳回None
count()
傳回查詢結果的數量
first_or_404()
傳回查詢的第1個結果,如果沒有結果,則終止請求,傳回404錯誤響應
get_or_404()
傳回指定主鍵對應的行,如果沒找到指定的主鍵,則終止請求,傳回404錯誤響應
paginate()
傳回一個paginate對象
基于關系的對象擷取
4.1 一對多
4.1.1 從“一”擷取對應的所有多端對象
注:inst_role.users擷取對象時,隐含了調用all()方法觸發執行,但如果像加一些過濾器(如排序),則需要在db.relationship中添加lazy='dynamic'關鍵字參數。然後即可引入過濾器:inst_role.users.order_by(Users.username)
4.2 多對多
在Flask-SQLAlchemy中,删除資料庫記錄,可映射至删除代表該記錄的對象:
參考資料