I、Flask數(shù)據(jù)關(guān)聯(lián)模型SQLAlchemy
Flask-SQLAlchemy使用起來(lái)非常有趣赚导,對(duì)于基本應(yīng)用十分容易使用赤惊,并且對(duì)于大型項(xiàng)目易于擴(kuò)展。有關(guān)完整的指南圈暗,請(qǐng)參見(jiàn)SQLAlchemy
API文檔裕膀。
常見(jiàn)情況下對(duì)于只有一個(gè) Flask 應(yīng)用,所有您需要做的事情就是創(chuàng)建 Flask 應(yīng)用,選擇加載配置接著創(chuàng)建 SQLAlchemy
對(duì)象時(shí)候把 Flask 應(yīng)用傳遞給它作為參數(shù)智绸。
一旦創(chuàng)建瞧栗,這個(gè)對(duì)象就包含 sqlalchemy
和 sqlalchemy.orm
中的所有函數(shù)和助手。此外它還提供一個(gè)名為 Model
的類迹恐,用于作為聲明模型時(shí)的 delarative 基類
普通的 SQLAlchemy 不同之處:
1.SQLAlchemy
允許您訪問(wèn)下面的東西 :
·sqlalchemy
和sqlalchemy.orm
下所有的函數(shù)和類
·一個(gè)叫做session
的預(yù)配置范圍的會(huì)話(session)
·metadata
屬性
·engine
屬性
·SQLAlchemy.create_all()
和SQLAlchemy.drop_all()
殴边,根據(jù)模型用來(lái)創(chuàng)建以及刪除表格的方法
· 一個(gè)Model
基類,即是一個(gè)已配置的聲明(declarative)的基礎(chǔ)(base)
2.Model
聲明基類行為類似一個(gè)常規(guī)的 Python 類锤岸,不過(guò)有個(gè)query
屬性是偷,可以用來(lái)查詢模型 (Model
和BaseQuery
)
3. 您必須提交會(huì)話,但是沒(méi)有必要在每個(gè)請(qǐng)求后刪除它(session)蛋铆,F(xiàn)lask-SQLAlchemy 會(huì)幫您完成刪除操作刺啦。
什么是ORM?
ORM是用來(lái)把對(duì)象模型表示的對(duì)象映射到基于SQL的關(guān)系模型數(shù)據(jù)庫(kù)結(jié)構(gòu)中去。簡(jiǎn)明點(diǎn)說(shuō)市咆,數(shù)據(jù)庫(kù)中每一行就是一個(gè)對(duì)象蒙兰。
II古毛、相關(guān)的配置字段
在上一章內(nèi),我們?cè)赿b_demo內(nèi)應(yīng)用課許多諸如app.config['SQLALCHEMY_DATABASE_URI'] = xxx
的語(yǔ)句鹰椒,這些語(yǔ)句為SQLAlchemy的配置方式
那么常見(jiàn)的配置選項(xiàng)有
選項(xiàng) | 說(shuō)明 |
---|---|
SQLALCHEMY_DATABASE_URI | 用于連接的數(shù)據(jù)庫(kù) URI 斤贰。例如:sqlite:////tmp/test.db 或 mysql://username:password@server/db |
SQLALCHEMY_BINDS | 一個(gè)映射 binds 到連接 URI 的字典殖侵。更多 binds 的信息見(jiàn) 用 Binds 操作多個(gè)數(shù)據(jù)庫(kù) 。 |
SQLALCHEMY_ECHO | 如果設(shè)置為 Ture 楞陷, SQLAlchemy 會(huì)記錄所有 發(fā)給 stderr 的語(yǔ)句茉唉,這對(duì)調(diào)試有用。 |
SQLALCHEMY_RECORD_QUERIES | 可以用于顯式地禁用或啟用查詢記錄艾凯。查詢記錄 在調(diào)試或測(cè)試模式自動(dòng)啟用懂傀。更多信息見(jiàn) get_debug_queries() 。 |
SQLALCHEMY_NATIVE_UNICODE | 可以用于顯式禁用原生 unicode 支持沧竟。當(dāng)使用不合適的指定無(wú)編碼的數(shù)據(jù)庫(kù)默認(rèn)值時(shí)缚忧,這對(duì)于 一些數(shù)據(jù)庫(kù)適配器是必須的(比如 Ubuntu 上某些版本的 PostgreSQL )。 |
SQLALCHEMY_POOL_SIZE | 數(shù)據(jù)庫(kù)連接池的大小糕非。默認(rèn)是引擎默認(rèn)值(通常 是 5 ) |
SQLALCHEMY_POOL_TIMEOUT | 設(shè)定連接池的連接超時(shí)時(shí)間。默認(rèn)是 10 禁筏。 |
SQLALCHEMY_POOL_RECYCLE | 多少秒后自動(dòng)回收連接衡招。這對(duì) MySQL 是必要的, 它默認(rèn)移除閑置多于 8 小時(shí)的連接州刽。注意如果 使用了 MySQL 浪箭, Flask-SQLALchemy 自動(dòng)設(shè)定這個(gè)值為 2 小時(shí)。 |
III匹表、常見(jiàn)數(shù)據(jù)模型字段
類型名 | python中類型 | 說(shuō)明 |
---|---|---|
Integer | int | 普通整數(shù)宣鄙,一般是32位 |
SmallInteger | int | 取值范圍小的整數(shù),一般是16位 |
BigInteger | int/long | 不限制精度的整數(shù) |
Float | float | 浮點(diǎn)數(shù) |
Numeric | decimal.Decimal | 普通整數(shù),一般是32位 |
String | str | 變長(zhǎng)字符串 |
Text | str | 變長(zhǎng)字符串敏簿,對(duì)較長(zhǎng)或不限長(zhǎng)度的字符串做了優(yōu)化 |
Unicode | unicode | 變長(zhǎng)Unicode字符串 |
UnicodeText | unicode | 變長(zhǎng)Unicode字符串惯裕,對(duì)較長(zhǎng)或不限長(zhǎng)度的字符串做了優(yōu)化 |
Boolean | bool | 布爾值 |
Date | datetime.date | 時(shí)間 |
Time | datetime.datetime | 日期和時(shí)間 |
LargeBinary | str | 二進(jìn)制文件 |
IV、約束字段與表間關(guān)系
由于Flask-SQLAlchemy不自動(dòng)設(shè)置表內(nèi)的字段的約束與撑刺,因此需要在設(shè)計(jì)數(shù)據(jù)字段時(shí)自行添加
那么常見(jiàn)的約束字段有
選項(xiàng)名 | 說(shuō)明 |
---|---|
primary_key | 如果為T(mén)rue握玛,代表表的主鍵 |
unique | 如果為T(mén)rue,代表這列不允許出現(xiàn)重復(fù)的值 |
index | 如果為T(mén)rue冕屯,為這列創(chuàng)建索引拂苹,提高查詢效率 |
nullable | 如果為T(mén)rue,允許有空值浴韭,如果為False,不允許有空值 |
default | 為這列定義默認(rèn)值 |
autoincrement | 自增 |
由于不自行設(shè)計(jì)id值(與Django的一定區(qū)別)Flask-SQLAlchemy并不涉及到表間關(guān)系的自動(dòng)配置泉粉,因此需要了解表間關(guān)系聯(lián)系字段
常見(jiàn)的表間關(guān)系字段有
選項(xiàng)名 | 說(shuō)明 |
---|---|
backref | 在關(guān)系的另一模型中添加反向引用,用于設(shè)置外鍵名稱,在1內(nèi)查n的 |
primary join | 明確指定兩個(gè)模型之間使用的聯(lián)結(jié)條件 |
uselist | 如果為False舍肠,不使用列表,而使用標(biāo)量值 |
order_by | 指定關(guān)系中記錄的排序方式 |
secondary | 指定多對(duì)多關(guān)系中關(guān)系表的名字 |
secondary join | 在SQLAlchemy中無(wú)法自行決定時(shí)叽躯,指定多對(duì)多關(guān)系中的二級(jí)聯(lián)結(jié)條件 |
lazy | 指定如何加載相關(guān)記錄肌括。 |
lazy的可選值有
屬性 | 說(shuō)明 |
---|---|
select | 首次訪問(wèn)時(shí)按需加載 |
immediate | 源對(duì)象加載后就加載 |
joined | 加載記錄谍夭,但使用聯(lián)結(jié) |
subquery | 立即加載,但使用子查詢 |
noload | 永不加載 |
dynamic | 不加載記錄紧索,但提供加載記錄的查詢 |
V珠漂、查詢執(zhí)行器
執(zhí)行器 | 說(shuō)明 |
---|---|
all() | 以列表形式返回查詢的所有結(jié)果 |
first() | 返回查詢的第一個(gè)結(jié)果,如果沒(méi)有結(jié)果荞彼,則返回 None |
first_or_404() | 返回查詢的第一個(gè)結(jié)果待笑,如果沒(méi)有結(jié)果,則終止請(qǐng)求寞缝,返回 404 錯(cuò)誤響應(yīng) |
get() | 返回指定主鍵對(duì)應(yīng)的行仰泻,如果沒(méi)有對(duì)應(yīng)的行,則返回 None |
get_or_404() | 返回指定主鍵對(duì)應(yīng)的行慎宾,如果沒(méi)找到指定的主鍵,則終止請(qǐng)求趟据,返回 404 錯(cuò)誤響應(yīng) |
count() | 返回查詢結(jié)果的數(shù)量 |
paginate() | 返回一個(gè) Paginate 對(duì)象汹碱,它包含指定范圍內(nèi)的結(jié)果 |
VI、過(guò)濾器
過(guò)濾器 | 說(shuō)明 |
---|---|
filter() | 把過(guò)濾器添加到原查詢上稚新,返回一個(gè)新查詢 |
filter_by() | 把等值過(guò)濾器添加到原查詢上跪腹,返回一個(gè)新查詢 |
limit | 使用指定的值限定原查詢返回的結(jié)果 |
offset() | 偏移原查詢返回的結(jié)果,返回一個(gè)新查詢 |
order_by() | 根據(jù)指定條件對(duì)原查詢結(jié)果進(jìn)行排序屯阀,返回一個(gè)新查詢 |
group_by() | 根據(jù)指定條件對(duì)原查詢結(jié)果進(jìn)行分組轴术,返回一個(gè)新查詢 |
注
filter_by用于查詢簡(jiǎn)單的列名,不支持比較運(yùn)算符盖袭。
比f(wàn)ilter_by的功能更強(qiáng)大彼宠,支持比較運(yùn)算符,支持or_醇蝴、in_等語(yǔ)法想罕。
VII霉涨、一個(gè)簡(jiǎn)單的建表實(shí)例
我們基于上一章中的db_demo.py的內(nèi)容展開(kāi),在每一個(gè)修改的實(shí)例內(nèi)增加一個(gè)__repr__
方法楼镐,簡(jiǎn)單介紹一下__repr__
:當(dāng)我們需要對(duì)一個(gè)函數(shù)中傳入某個(gè)參數(shù)后的狀態(tài)需要打包打印往枷,或者顯示在屏幕上時(shí)我們可以在這個(gè)函數(shù)內(nèi)部定義一個(gè)__repr__
函數(shù)凄杯,可以通過(guò)這個(gè)函數(shù)戒突,對(duì)你想要打印的內(nèi)容進(jìn)行設(shè)置描睦。
修改db_demo.py
class Role(db.Model):
# 定義表名
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
db.relationship('User', backref='role')
def __repr__(self):
return 'Role:{}'.format(self.name)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
email = db.Column(db.String(64), unique=True)
password = db.Column(db.String(64), unique=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return 'User:{}'.format(self.name)
VIII、一些查詢的聯(lián)系
在此前隔崎,我們修改db_demo內(nèi)的main韵丑,以用來(lái)插入一些數(shù)據(jù),當(dāng)然技潘,這種方式并非日后的常用方法千康,這里只作為一種簡(jiǎn)單的填充
if __name__ == '__main__':
# 刪除表
db.drop_all()
# 創(chuàng)建表
db.create_all()
ro1 = Role(name='admin')
ro2 = Role(name='user')
db.session.add_all([ro1, ro2])
db.session.commit()
us1 = User(name='wang', email='wang@163.com', password='123456', role_id=ro1.id)
us2 = User(name='zhang', email='zhang@189.com', password='201512', role_id=ro2.id)
us3 = User(name='chen', email='chen@126.com', password='987654', role_id=ro2.id)
us4 = User(name='zhou', email='zhou@163.com', password='456789', role_id=ro1.id)
us5 = User(name='tang', email='tang@neuedu.com', password='158104', role_id=ro2.id)
us6 = User(name='wu', email='wu@gmail.com', password='5623514', role_id=ro2.id)
us7 = User(name='qian', email='qian@gmail.com', password='1543567', role_id=ro1.id)
us8 = User(name='liu', email='liu@neuedu.com', password='867322', role_id=ro1.id)
us9 = User(name='li', email='li@163.com', password='4526342', role_id=ro2.id)
us10 = User(name='sun', email='sun@163.com', password='235523', role_id=ro2.id)
db.session.add_all([us1, us2, us3, us4, us5, us6, us7, us8, us9, us10])
db.session.commit()
db.session.add_all([us1, us2])
db.session.commit()
app.run(port=5005,debug=True)
1拾弃、返回名字等于wang 的所有人
User.query.filter_by(name='wang').all()
2、返回查詢的第一個(gè)對(duì)象
User.query.first()
3奔坟、返回所有查詢對(duì)象
User.query.all()
4搭盾、返回名字結(jié)尾字符為g的所有數(shù)據(jù)
User.query.filter(User.name.endswith("g")).all()
User.query.filter(User.name.startswith("w")).all()
User.query.filter(User.name.contains("n")).all()
User.query.filter(User.name.like("%n%g")).all()
5、返回user中id = 1的數(shù)據(jù)
User.query.get(1)
6澜建、返回名字不等于wang的所有數(shù)據(jù)
User.query.filter(not_(User.name == "wang")).all()
User.query.filter(User.name != "wang").all()
7蝌以、返回名字不等于 wang 且 郵箱 以 163.com結(jié)尾的所有
User.query.filter(or_(User.name != 'wang',User.email.endswith('163.com') )).all()
8跟畅、查詢名字和郵箱都以 li 開(kāi)頭的所有數(shù)據(jù)
User.query.filter(User.name.startswith("li"), User.email.startswith("li")).all()
User.query.filter(and_(User.name.startswith("li"), User.email.startswith("li"))).all()
9、查詢password是 123456
或者 email
以 neuedu.com
結(jié)尾的所有數(shù)據(jù)
User.query.filter(or_(User.password == '123456', User.email.endswith('neuedu.com'))).all()
10奸攻、查詢id為 [1, 3, 5, 7, 9] 的用戶列表
User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all()
11、 查詢name為liu的角色數(shù)據(jù)
User.query.filter(User.name == 'liu').all()
12睹耐、查詢所有用戶數(shù)據(jù),并以郵箱排序
User.query.order_by(User.email.desc()).all()
13占拍、每頁(yè)3個(gè)捎迫,查詢第2頁(yè)的數(shù)據(jù)
pn = User.query.paginate(2, 3)
pn.items # 獲取該頁(yè)內(nèi)容
pn.page # 獲取該頁(yè)頁(yè)碼
pn.pages # 獲取總頁(yè)數(shù)
14窄绒、定義模型,并顯示作者名和書(shū)名
作者Author()彰导,表名 author位谋,屬性有:id,name掏父,au_book(外鍵)
書(shū)名Book()赊淑,表名 books ,屬性有:id陶缺,name饱岸,au_book
class Author(db.Model):
__tablename__ = 'author'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
au_book = db.Column(db.Integer, db.ForeignKey('book.id'))
def __repr__(self):
return 'Author:{}'.format(self.name)
class Book(db.Model):
__tablename__ = 'books'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
au_book = db.relationship('Book', backref='book')
def __repr__(self):
return 'Book:{}'.format(self.name)
IX、增刪改的操作
Flask-SQLAlchemy的增刪改操作可以理解為是通過(guò)與數(shù)據(jù)庫(kù)建立一個(gè)小型會(huì)話的方式進(jìn)行的蛛砰,下面介紹一下這些基于db = SQLAlchemy(app)
的操作
操作 | 說(shuō)明 |
---|---|
create_all() | 建表操作黍衙,db.create_all() |
drop_all() | 刪除表操作荠诬,db.drop_all() |
add_all() | 插入操作位仁,db.seesion.add_all([])聂抢,插入可選擇使用list進(jìn)行批量插入 |
commit() | 提交操作棠众,db.session.commit(),提交其語(yǔ)句以上的所有更改數(shù)據(jù)庫(kù)內(nèi)容的操作 |
delete() | 刪除操作空盼,db.session.delete()新荤,刪除一條數(shù)據(jù) |
更新操作只需要選擇需要更新的對(duì)象,修改其屬性值然后提交即可篱瞎。