第七章 使用 Flask 擴展管理數(shù)據(jù)庫

Flask-SQLAlchemy是一個Flask的一個擴展覆山,簡化了在Flask程序中使用SQLAlchemy的操作難度。SQLAlchemy是一個很強大的關(guān)系型數(shù)據(jù)庫框架,支持多種數(shù)據(jù)庫后臺。SQLAlchemy提供了高層ORM薯酝,也提供了使用數(shù)據(jù)庫原生SQL的低層功能。而Flask-Migrate擴展是對SQLAlchemy的主力開發(fā)人員編寫的Alembic遷移框架的一個輕量級包裝爽柒,并集成到了Flask-Script中蜜托。

使用pip安裝Flask的擴展

(flask)$ pip install flask-sqlalchemy
(flask)$ pip install flask-migrate

使用flask-sqlalchemy管理數(shù)據(jù)庫

Flask-Sqlalchemy數(shù)據(jù)庫URL

數(shù)據(jù)庫引擎 URL
MySQL mysql://username:password@hostname/database
Postgres postgresql://username:password@hostname/database
SQLite(Unix) sqlite:////absolute/path/database
SQLite(Windows) sqlite:///c:/absolute/path/database

在這些URL中,hostname表示MySQL服務(wù)器所在的主機霉赡,可以是本地主機(localhost)橄务,也可以是遠程服務(wù)器。數(shù)據(jù)服務(wù)器上可以托管多個數(shù)據(jù)庫穴亏,因此database表示要是使用的數(shù)據(jù)庫名蜂挪。如果數(shù)據(jù)庫需要進行認證,username和password表示數(shù)據(jù)庫用戶和密碼嗓化。

程序中使用的數(shù)據(jù)庫URL必須保存到Flask配置對象config.py的SQLALCHEMY_DATABASE_URI鍵中棠涮。另外一個很有用的選項,即SQLALCHEMY_COMMIT_ON_TEARDOWN 鍵刺覆,將其設(shè)置為True時严肪,每次請求結(jié)束后都會自動提交數(shù)據(jù)庫中的變動。

定義數(shù)據(jù)模型

和所有的應(yīng)用一樣,我們先設(shè)計簡單的用戶和權(quán)限模型驳糯。模型這個術(shù)語表示程序中使用的持久化實體篇梭。在 ORM 中,模型一般是一個 Python 類,類中的屬性對應(yīng)數(shù)據(jù)庫表中的列。
示例 2-1 app/models.py: 定義 Role 和 User 模型

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return '<Role %r>' % self.name

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    def __repr__(self):
        return '<User %r>' % self.username

類變量tablename定義在數(shù)據(jù)庫中使用的表名酝枢。其余的類變量都是該模型的屬性恬偷,被定義為db.Column類的示例。

db.Column類構(gòu)造函數(shù)的第一個參數(shù)是數(shù)據(jù)庫列和模型屬性的類型帘睦。下表列出了一些最常用的列類型以及在模型中使用的Python類型袍患。

最常用的AQLAlchemy列類型

類型名 Python類型 說明
Integer int 普通整數(shù),一般是32位
SmallInteger int 取值范圍小的整數(shù)竣付,一般是16位
BigInteger int或long 不限制精度的整數(shù)
Float float 浮點數(shù)
Numeric decimal.Decimal 定點數(shù)
String str 變長字符串
Text str 變長字符串诡延,對較長或不限長度的字符串做了優(yōu)化
Unicode unicode 變長Unicode字符串
UnicodeText unicode 變長Unicode字符串,對較長或不限長度的字符床做了優(yōu)化
Boolean bool 布爾值
Date datetime.date 日期
Time datetime.time 時間
DateTime datetime.datetime 日期和時間
Interval datetime.timedeta 時間間隔
Enum str 一組字符串
PickleType 任何Python對象 自動化使用Pickle序列化
LargeBinary str 二進制文件

最常用的SQLAlchemy列選項

選項名 說明
primary_key 如果設(shè)為True,這列就是表的主鍵
unique 如果設(shè)為True古胆,這列不允許出現(xiàn)重復(fù)
index 如果設(shè)為True肆良,為這列創(chuàng)建索引,提升查詢效率
nullable 如果設(shè)為True赤兴,這列允許使用空值;如果設(shè)為False隧哮,這列不允許使用空值
default 為這列定義默認值

關(guān)系

app/models.py 一對多關(guān)系模型

class Role(db.Model):
    # ...
    users = db.relationship('User', backref='role', lazy='dynamic')

class User(UserMixin, db.Model):
    # ...
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

常用的SQLAlchemy關(guān)系選項

選項名 說明
backref 在關(guān)系的另一個模型中添加反向引用
primaryjoin 明確指定兩個模型之間使用的聯(lián)結(jié)條件桶良。只在模棱兩可的關(guān)系中需要指定
lazy 指定如何加載相關(guān)記錄【谙瑁可選值有select(首次訪問時按需加載)陨帆、immediate(源對象加載后加載)、joined(加載記錄采蚀,但使用聯(lián)結(jié))疲牵、subquery(立即加載,但使用子查詢)榆鼠,noload(永不加載)和dynamic(不加載記錄纲爸,但提供加載記錄的查詢)
uselist 如果設(shè)為False,不使用列表妆够,而使用標量值
order_by 指定關(guān)系中記錄的排序方式
secondary 指定多對多關(guān)系表的名字
secondaryjoin SQLAlchemy無法自行決定時识啦,指定多對多關(guān)系中的二級聯(lián)結(jié)條件

一對多 關(guān)系之外,還有幾種其它的關(guān)系類型神妹。一對一 關(guān)系可以用前面介紹的 一對多 關(guān)系表示颓哮,但調(diào)用db.relationship()時要把uselist設(shè)為False,把“多”變成“一”鸵荠。 多對一 關(guān)系也可使用 一對多 表示冕茅,對調(diào)兩個表即可,或者把外鍵和db.relationship()都放在“多”這一側(cè)。最復(fù)雜的關(guān)系是 多對多 姨伤,需要用到第三張表哨坪,這個表稱為 關(guān)系表

創(chuàng)建遷移倉庫

定義好數(shù)據(jù)模型之后姜挺,就應(yīng)該設(shè)計數(shù)據(jù)庫了齿税,不管你用mysql還是sqlite,flask-SQLAlchemy和flask-Migrate會根據(jù)數(shù)據(jù)模型自動創(chuàng)建數(shù)據(jù)庫炊豪。
為了導(dǎo)出數(shù)據(jù)庫遷移命令凌箕,flask-Migrate提供了一個MigrateCommand類,可附加在flask-script的manage對象上词渤。設(shè)置manage.py牵舱。

...
from flask.ext.migrate import Migrate, MigrateCommand

...

migrate = Migrate(app=app, db=db)
manager.add_command('db', MigrateCommand)

使用一下命令創(chuàng)建數(shù)據(jù)庫及遷移倉庫

(flask)$ python manage.py db init
(flask)$ python manage.py db migrate -m "initial migration"
(flask)$ python manage.py db upgrade

注意事項,雖然flask-migrate提供了downgrade()函數(shù)缺虐,可以將改動刪除芜壁。但是建議大家不要隨便使用。如果你覺得數(shù)據(jù)庫設(shè)計得有問題高氮,建議你刪除相關(guān)數(shù)據(jù)庫設(shè)計慧妄,重新再來。

數(shù)據(jù)庫操作

插入數(shù)據(jù)

user = User(username=u'張三')
db.session.add(user)  # 插入數(shù)據(jù)
db.session.commit()  # 只有提交事務(wù)了剪芍,才可以獲取(user.id)數(shù)據(jù)的ID值塞淹。

查詢數(shù)據(jù)

a. 用主鍵獲取數(shù)據(jù):

User.query.get(1)
<User u'張三'>

b. 通過一個精確參數(shù)進行反查:

peter=User.query.filter_by(username='peter').first()  #注意:精確查詢函數(shù)query.filter_by(),是通過傳遞參數(shù)進行查詢罪裹;其他增強型查詢函數(shù)是query.filter()饱普,通過傳遞表達式進行查詢。
print(peter.id)  #如果數(shù)據(jù)不存在則返回None

c. 模糊查詢:

User.query.filter(User.email.endswith('@example.com')).all()
[<User u'admin'>, <User u'guest'>]

c1. 邏輯非1:

peter = User.query.filter(User.username != 'peter').first()
print(peter.id)

c2. 邏輯非2:

from sqlalchemy import not_
peter = User.query.filter(not_(User.username=='peter')).first()
print(peter.id)

c3. 邏輯與:

from sqlalchemy import and_
peter = User.query.filter(and_(User.username=='peter', User.email.endswith('@example.com'))).first()
print(peter.id)

c4. 邏輯或:

from sqlalchemy import or_
peter = User.query.filter(or_(User.username != 'peter', User.email.endswith('@example.com'))).first()
print(peter.id)

d. 查詢數(shù)據(jù)加工

d1. 排序:

User.query.order_by(User.username)  #嘿嘿状共,你用哪個字段作為排序參考呢套耕?
[<User u'admin'>, <User u'guest'>, <User u'peter'>]

d2. 限制返回的數(shù)目:

User.query.limit(1).all()
[<User u'admin'>]

e. 查詢數(shù)據(jù)返回

e1. 返回查詢到的第一個對象:

r = User.query.first()
print(r)

e2. 返回所有查詢到的對象:

r = User.query.all()
print(r)

常用的SQLAlchemy查詢過濾器

過濾器 說明
filter() 把過濾器添加到原查詢上,返回一個新查詢
filter_by() 把等值過濾器添加到原查詢上峡继,返回一個新查詢
limit() 使用指定的值限制原查詢返回的結(jié)果數(shù)量冯袍,返回一個新查詢
offset() 偏移原查詢返回的結(jié)果,返回一個新查詢
order_by() 根據(jù)指定條件對原查詢結(jié)果進行排序碾牌,返回一個新查詢
group_by() 根據(jù)指定條件對原查詢進行分組颠猴,返回一個新查詢

在查詢上應(yīng)用指定的過濾器后,通過調(diào)用all()執(zhí)行查詢小染,以列表形式返回結(jié)果翘瓮。除了all(),還有其它方法能觸發(fā)查詢執(zhí)行裤翩。

最常用的SQLAlchemy查詢執(zhí)行函數(shù)

方法 說明
all() 以列表形式返回查詢的所有結(jié)果
first() 返回查詢的第一個結(jié)果资盅,如果沒有結(jié)果调榄,返回None
first_or_404() 返回查詢的第一個結(jié)果,如果沒有結(jié)果呵扛,則終止請求每庆,返回404錯誤響應(yīng)
get() 返回指定主鍵對應(yīng)的行,如果沒有對應(yīng)的行今穿,返回None
get_or_404() 返回指定主鍵對應(yīng)的行缤灵,如果沒有對應(yīng)的行,則終止請求蓝晒,返回404錯誤響應(yīng)
count() 返回查詢結(jié)果的數(shù)量
paginate() 返回一個Paginate對象腮出,它包含指定范圍內(nèi)的結(jié)果。

刪除數(shù)據(jù)

u = User.query.first()
db.session.delete(u)  #刪除數(shù)據(jù)和插入數(shù)據(jù)一樣簡單芝薇,但必須是通過查詢返回的對象胚嘲。
db.session.commit()

更新數(shù)據(jù)

u = User.query.first()
u.username = 'guest'  #更新數(shù)據(jù)和變量賦值那么簡單,但必須是通過查詢返回的對象洛二。
db.session.commit()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末馋劈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晾嘶,更是在濱河造成了極大的恐慌妓雾,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垒迂,死亡現(xiàn)場離奇詭異械姻,居然都是意外死亡,警方通過查閱死者的電腦和手機娇斑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門策添,熙熙樓的掌柜王于貴愁眉苦臉地迎上來材部,“玉大人毫缆,你說我怎么就攤上這事±值迹” “怎么了苦丁?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長物臂。 經(jīng)常有香客問我旺拉,道長,這世上最難降的妖魔是什么棵磷? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任蛾狗,我火速辦了婚禮,結(jié)果婚禮上仪媒,老公的妹妹穿的比我還像新娘沉桌。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布留凭。 她就那樣靜靜地躺著佃扼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔼夜。 梳的紋絲不亂的頭發(fā)上兼耀,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音求冷,去河邊找鬼瘤运。 笑死,一個胖子當(dāng)著我的面吹牛遵倦,可吹牛的內(nèi)容都是我干的尽超。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼梧躺,長吁一口氣:“原來是場噩夢啊……” “哼似谁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掠哥,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤巩踏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后续搀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塞琼,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年禁舷,在試婚紗的時候發(fā)現(xiàn)自己被綠了彪杉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡牵咙,死狀恐怖派近,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洁桌,我是刑警寧澤渴丸,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站另凌,受9級特大地震影響谱轨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吠谢,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一土童、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧工坊,春花似錦献汗、人聲如沸错沃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽枢析。三九已至,卻和暖如春刃麸,著一層夾襖步出監(jiān)牢的瞬間醒叁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工泊业, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留把沼,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓吁伺,卻偏偏與公主長得像饮睬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子篮奄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容