Python的Flask框架中使用Flask-SQLAlchemy管理數(shù)據(jù)庫(kù)的教程
使用Flask-SQLAlchemy管理數(shù)據(jù)庫(kù)
Flask-SQLAlchemy是一個(gè)Flask擴(kuò)展,它簡(jiǎn)化了在Flask應(yīng)用程序中對(duì)SQLAlchemy的使用。SQLAlchemy是一個(gè)強(qiáng)大的關(guān)系數(shù)據(jù)庫(kù)框架,支持一些數(shù)據(jù)庫(kù)后端。提供高級(jí)的ORM和底層訪問數(shù)據(jù)庫(kù)的本地SQL功能。
和其他擴(kuò)展一樣,通過pip安裝Flask-SQLAlchemy:
(venv) $ pip install flask-sqlalchemy
在Flask-SQLAlchemy,數(shù)據(jù)庫(kù)被指定為URL。表格列出三個(gè)最受歡迎的數(shù)據(jù)庫(kù)引擎url的格式:
在這些URL中,hostname是指托管MySQL服務(wù)的服務(wù)器,可能是本地(localhost)又或是遠(yuǎn)程服務(wù)器。數(shù)據(jù)庫(kù)服務(wù)器可以托管多個(gè)數(shù)據(jù)庫(kù),所以database指出要使用的數(shù)據(jù)庫(kù)名。數(shù)據(jù)庫(kù)需要身份驗(yàn)證,username和 password是數(shù)據(jù)庫(kù)用戶憑證。
注:> SQLite數(shù)據(jù)庫(kù)沒有服務(wù),所以hostname、username和password可以缺省且數(shù)據(jù)庫(kù)是一個(gè)磁盤文件名。
應(yīng)用程序數(shù)據(jù)庫(kù)URL必須在Flask配置對(duì)象中的SQLALCHEMY_DATABASE_URI鍵中進(jìn)行配置。另一個(gè)有用的選項(xiàng)是SQLALCHEMY_COMMIT_ON_TEARDOWN,可以設(shè)置為True來啟用自動(dòng)提交數(shù)據(jù)庫(kù)更改在每個(gè)請(qǐng)求中。查閱Flask-SQLAlchemy文檔獲取更多其他配置選項(xiàng)。
from flask.ext.sqlalchemy import SQLAlchemy basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] =\ 'sqlite:///' + os.path.join(basedir, 'data.sqlite') app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True db = SQLAlchemy(app)
由SQLAlchemy實(shí)例化的db對(duì)象表示數(shù)據(jù)庫(kù)且提供訪問Flask-SQLAlchemy的所有功能。
模型定義
模型是指由應(yīng)用程序使用的持久化實(shí)體。在ORM的背景下,一個(gè)模型通常是一個(gè)帶有屬性的Python類,其屬性與數(shù)據(jù)庫(kù)表的列相匹配對(duì)應(yīng)。Flask-SQLAlchemy數(shù)據(jù)庫(kù)實(shí)例提供了一個(gè)基類以及一組輔助類和函數(shù)用于定義它的結(jié)構(gòu)。
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ù)庫(kù)中表的名稱。如果__tablename__缺省Flask-SQLAlchemy會(huì)指定默認(rèn)的表名,但是這些缺省名稱不遵守使用復(fù)數(shù)命名的約定,所以最好是顯式命名表名。其余的變量是模型的屬性,被定義為db.Column類的實(shí)例。
傳給db.Column構(gòu)造函數(shù)的第一個(gè)參數(shù)是數(shù)據(jù)庫(kù)列的類型也就是模型屬性的數(shù)據(jù)類型。表格5-2列出一些可用的列的類型,也是用于模型中的Python類型。
最常見的SQLAlchemy列類型
db.Column剩余的參數(shù)為每個(gè)屬性指定了配置選項(xiàng)。
最常見的SQLAlchemy列選項(xiàng)
注:Flask-SQLAlchemy需要給所有的模型定義主鍵列,通常命名為id。
兩個(gè)模型都包含了repr()方法來給它們顯示一個(gè)可讀字符串,雖然不是完全必要,不過用于調(diào)試和測(cè)試還是很不錯(cuò)的。
關(guān)系
關(guān)系數(shù)據(jù)庫(kù)通過使用關(guān)系在不同的表中建立連接。關(guān)系圖表達(dá)了用戶和用戶角色之間的簡(jiǎn)單關(guān)系。這個(gè)角色和用戶是一對(duì)多關(guān)系,因?yàn)橐粋€(gè)角色可以從屬于多個(gè)用戶,而一個(gè)用戶只能擁有一個(gè)角色。
下面的模型類展示了中表達(dá)的一對(duì)多關(guān)系。
class Role(db.Model): # ... users = db.relationship('User', backref='role') class User(db.Model): # ... role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
關(guān)系通過使用外鍵來連接兩行。添加給User模型的role_id列被定義為外鍵,且建立關(guān)系。db.ForeignKey()的參數(shù)roles.id指定的列應(yīng)該理解為在roles表的行中持有id值的列。
添加到Role模型的users屬性表現(xiàn)了關(guān)系的面向?qū)ο蟮挠^點(diǎn)。給定Role類的實(shí)例,users屬性會(huì)返回一組連接到該角色的用戶。指定給db.relationship()的第一個(gè)參數(shù)表明模型中關(guān)系的另一邊。如果類還未定義,這個(gè)模型可以作為字符串提供。
注意:之前在segmentdefault中遇到的問題,后來粗略閱讀了SQLAlchemy的源碼。ForeignKey類的column接收三種類型的參數(shù),一種是“模型名.屬性名”;一種是“表名.列名”,最后一種沒看明白,下次試著用一下。
db.relationship()的backref參數(shù)通過給User模型增加role屬性來定義反向關(guān)系。這個(gè)屬性可以替代role_id訪問Role模型,是作為對(duì)象而不是外鍵。
大多數(shù)情況下db.relationship()可以定位自己的外鍵關(guān)系,但是有時(shí)候不能確定哪個(gè)列被用作外鍵。例如,如果User模型有兩個(gè)或更多列被定義為Role的外鍵,SQLAlchemy將不知道使用兩個(gè)中的哪一個(gè)。每當(dāng)外鍵配置模棱兩可的時(shí)候,就必須使用額外參數(shù)db.relationship()。下標(biāo)列出一些常用配置選項(xiàng)用于定義關(guān)系:
常用SQLAlchemy關(guān)系選項(xiàng)
建議:如果你有克隆在GitHub上的應(yīng)用程序,你現(xiàn)在可以運(yùn)行g(shù)it checkout 5a來切換到這個(gè)版本的應(yīng)用程序。
除了一對(duì)多關(guān)系還有其他種類關(guān)系。一對(duì)一關(guān)系可以表述為前面描述的一對(duì)多關(guān)系,只要將db.relationship()中的uselist選項(xiàng)設(shè)置為False,“多”就變?yōu)椤耙弧绷恕6鄬?duì)一關(guān)系也可表示為將表反轉(zhuǎn)后的一對(duì)多關(guān)系,或表示為外鍵和db.relationship()定義在“多”那邊。最復(fù)雜的關(guān)系類型,多對(duì)多,需要一個(gè)被稱作關(guān)聯(lián)表的額外表。你將在第十二章學(xué)習(xí)多對(duì)多關(guān)系。
數(shù)據(jù)庫(kù)操作
學(xué)習(xí)怎樣使用模型的最好方式就是使用Python shell。以下部分將介紹最常見的數(shù)據(jù)庫(kù)操作。
創(chuàng)建表
首先要做的第一件事情就是指示Flask-SQLAlchemy基于模型類創(chuàng)建數(shù)據(jù)庫(kù)。db.create_all()函數(shù)會(huì)完成這些:
(venv) $ python hello.py shell >>> from hello import db >>> db.create_all()
如果你檢查應(yīng)用程序目錄,你會(huì)發(fā)現(xiàn)名為data.sqlite的新文件,SQLite數(shù)據(jù)庫(kù)名在配置中給出。如果數(shù)據(jù)庫(kù)已存在db.create_all()函數(shù)不會(huì)重新創(chuàng)建或更新數(shù)據(jù)庫(kù)表。這會(huì)非常的不方便當(dāng)模型被修改且更改需要應(yīng)用到現(xiàn)有的數(shù)據(jù)庫(kù)時(shí)。更新現(xiàn)有的數(shù)據(jù)庫(kù)表的蠻力解決方案是先刪除舊的表:
>>> db.drop_all() >>> db.create_all()
不幸的是,這種方法有個(gè)不受歡迎的副作用就是摧毀舊的數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)。更新數(shù)據(jù)庫(kù)問題的解決方案會(huì)在這章快結(jié)束的時(shí)候介紹。
插入行
下面的示例會(huì)創(chuàng)建新的角色和用戶:
>>> from hello import Role, User >>> admin_role = Role(name='Admin') >>> mod_role = Role(name='Moderator') >>> user_role = Role(name='User') >>> user_john = User(username='john', role=admin_role) >>> user_susan = User(username='susan', role=user_role) >>> user_david = User(username='david', role=user_role)
模型的構(gòu)造函數(shù)接受模型屬性的初始值作為關(guān)鍵字參數(shù)。注意,甚至可以使用role屬性,即使它不是一個(gè)真正的數(shù)據(jù)庫(kù)列,而是一對(duì)多關(guān)系的高級(jí)表示。這些新對(duì)象的id屬性沒有顯式設(shè)置:主鍵由Flask-SQLAlchemy來管理。到目前為止對(duì)象只存于Python中,他們還沒有被寫入數(shù)據(jù)庫(kù)。因?yàn)樗麄兊膇d值尚未分配:
>>> print(admin_role.id) None >>> print(mod_role.id) None >>> print(user_role.id) None
修改數(shù)據(jù)庫(kù)的操作由Flask-SQLAlchemy提供的db.session數(shù)據(jù)庫(kù)會(huì)話來管理。準(zhǔn)備寫入到數(shù)據(jù)庫(kù)中的對(duì)象必須添加到會(huì)話中:
>>> db.session.add(admin_role) >>> db.session.add(mod_role) >>> db.session.add(user_role) >>> db.session.add(user_john) >>> db.session.add(user_susan) >>> db.session.add(user_david)
或,更簡(jiǎn)潔的:
>>> db.session.add_all([admin_role, mod_role, user_role, ... user_john, user_susan, user_david])為了寫對(duì)象到數(shù)據(jù)庫(kù),需要通過它的commit()方法來提交會(huì)話:
>>> db.session.commit()
再次檢查id屬性;這個(gè)時(shí)候它們都已經(jīng)被設(shè)置好了:
>>> print(admin_role.id) 1 >>> print(mod_role.id) 2 >>> print(user_role.id) 3
注:db.session數(shù)據(jù)庫(kù)會(huì)話和第四章討論的Flask會(huì)話沒有任何聯(lián)系。數(shù)據(jù)庫(kù)會(huì)話也叫事務(wù)。
數(shù)據(jù)庫(kù)會(huì)話在數(shù)據(jù)庫(kù)一致性上是非常有用的。提交操作會(huì)原子性地將所有添加到會(huì)話中的對(duì)象寫入數(shù)據(jù)庫(kù)。如果在寫入的過程發(fā)生錯(cuò)誤,會(huì)將整個(gè)會(huì)話丟棄。如果你總是在一個(gè)會(huì)話提交相關(guān)修改,你必須保證避免因部分更新導(dǎo)致的數(shù)據(jù)庫(kù)不一致的情況。
注:數(shù)據(jù)庫(kù)會(huì)話也可以回滾。如果調(diào)用db.session.rollback(),任何添加到數(shù)據(jù)庫(kù)會(huì)話中的對(duì)象都會(huì)恢復(fù)到它們?cè)?jīng)在數(shù)據(jù)庫(kù)中的狀態(tài)。
修改行
數(shù)據(jù)庫(kù)會(huì)話中的add()方法同樣可以用于更新模型。繼續(xù)在同一shell會(huì)話中,下面的示例重命名“Admin”角色為“Administrator”:
>>> admin_role.name = 'Administrator' >>> db.session.add(admin_role) >>> db.session.commit()
注意:不過貌似我們?cè)谧龈虏僮鞯臅r(shí)候都不使用db.session.add(),而是直接使用db.session.commit()來提交事務(wù)。
刪除行
數(shù)據(jù)庫(kù)會(huì)話同樣有delete()方法。下面的示例從數(shù)據(jù)庫(kù)中刪除“Moderator”角色:
>>> db.session.delete(mod_role) >>> db.session.commit()
注意刪除,和插入更新一樣,都是在數(shù)據(jù)庫(kù)會(huì)話提交后執(zhí)行。
返回行
Flask-SQLAlchemy為每個(gè)模型類創(chuàng)建一個(gè)query對(duì)象。最基本的查詢模型是返回對(duì)應(yīng)的表的全部?jī)?nèi)容:
>>> Role.query.all() [<Role u'Administrator'>, <Role u'User'>] >>> User.query.all() [<User u'john'>, <User u'susan'>, <User u'david'>]
使用過濾器可以配置查詢對(duì)象去執(zhí)行更具體的數(shù)據(jù)庫(kù)搜索。下面的例子查找所有被分配“User”角色的用戶:
>>> User.query.filter_by(role=user_role).all() [<User u'susan'>, <User u'david'>]
對(duì)于給定的查詢還可以檢查SQLAlchemy生成的原生SQL查詢,并將查詢對(duì)象轉(zhuǎn)換為一個(gè)字符串:
>>> str(User.query.filter_by(role=user_role)) 'SELECT users.id AS users_id, users.username AS users_username, users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'
如果你退出shell會(huì)話,在前面的示例中創(chuàng)建的對(duì)象將不能作為Python對(duì)象而存在,但可繼續(xù)作為行記錄存在各自的數(shù)據(jù)庫(kù)表中。如果你開始一個(gè)全新的shell會(huì)話,你必須從它們的數(shù)據(jù)庫(kù)行中重新創(chuàng)建Python對(duì)象。下面的示例執(zhí)行查詢來加載名字為“User”的用戶角色。
>>> user_role = Role.query.filter_by(name='User').first()
過濾器如filter_by()通過query對(duì)象來調(diào)用,且返回經(jīng)過提煉后的query。多個(gè)過濾器可以依次調(diào)用直到需要的查詢配置結(jié)束為止。
下面展示一些查詢中常用的過濾器。
在需要的過濾器已經(jīng)全部運(yùn)用于query后,調(diào)用all()會(huì)觸發(fā)query執(zhí)行并返回一組結(jié)果,但是除了all()以外還有其他方式可以觸發(fā)執(zhí)行。常用SQLAlchemy查詢執(zhí)行器:
關(guān)系的原理類似于查詢。下面的示例從兩邊查詢角色和用戶之間的一對(duì)多關(guān)系:
>>> users = user_role.users >>> users [<User u'susan'>, <User u'david'>] >>> users[0].role <Role u'User'>
此處的user_role.users查詢有點(diǎn)小問題。當(dāng)user_role.users表達(dá)式在內(nèi)部調(diào)用all()時(shí)通過隱式查詢執(zhí)行來返回用戶的列表。因?yàn)椴樵儗?duì)象是隱藏的,是不可能通過附加查詢過濾器進(jìn)一步提取出來。在這個(gè)特定的例子中,它可能是用于按字母排列順序返回用戶列表。在下面的示例中,被lazy = 'dynamic'參數(shù)修改過的關(guān)系配置的查詢是不會(huì)自動(dòng)執(zhí)行的。
app/models.py:動(dòng)態(tài)關(guān)系
class Role(db.Model): # ... users = db.relationship('User', backref='role', lazy='dynamic') # ...
用這種方式配置關(guān)系,user_roles.user查詢還沒有執(zhí)行,所以可以給它增加過濾器:
>>> user_role.users.order_by(User.username).all() [<User u'david'>, <User u'susan'>] >>> user_role.users.count() 2
相關(guān)文章
使用Python字符串訪問與修改局部變量的實(shí)現(xiàn)代碼
這篇文章主要介紹了使用Python字符串訪問與修改局部變量,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06python中判斷文件編碼的chardet(實(shí)例講解)
下面小編就為大家分享一篇python中判斷文件編碼的chardet(實(shí)例講解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12Python3 關(guān)于pycharm自動(dòng)導(dǎo)入包快捷設(shè)置的方法
今天小編就為大家分享一篇Python3 關(guān)于pycharm自動(dòng)導(dǎo)入包快捷設(shè)置的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-01-01Python curses內(nèi)置顏色用法實(shí)例
在本篇文章里小編給大家整理的是一篇關(guān)于Python curses內(nèi)置顏色用法實(shí)例內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-06-06python實(shí)時(shí)監(jiān)控cpu小工具
這篇文章主要為大家詳細(xì)介紹了python實(shí)時(shí)監(jiān)控cpu的小工具,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06