大部分程序都須要保存數據,因此不可避免要使用數據庫。用來操做數據庫的數據庫管理系統(DBMS)有不少選擇,對於不一樣類型的程序,不一樣的使用場景,都會有不一樣的選擇。在這個教程中,咱們選擇了屬於關係型數據庫管理系統(RDBMS)的 SQLite,它基於文件,不須要單獨啓動數據庫服務器,適合在開發時使用,或是在數據庫操做簡單、訪問量低的程序中使用。html
爲了簡化數據庫操做,咱們將使用 SQLAlchemy——一個 Python 數據庫工具(ORM,即對象關係映射)。藉助 SQLAlchemy,你能夠經過定義 Python 類來表示數據庫裏的一張表(類屬性表示表中的字段 / 列),經過對這個類進行各類操做來代替寫 SQL 語句。這個類咱們稱之爲模型類,類中的屬性咱們將稱之爲字段。python
Flask 有大量的第三方擴展,這些擴展能夠簡化和第三方庫的集成工做。咱們下面將使用一個叫作 Flask-SQLAlchemy 的官方擴展來集成 SQLAlchemy。git
首先使用 Pipenv 安裝它:github
$ pipenv install flask-sqlalchemy複製代碼
大部分擴展都須要執行一個「初始化」操做。你須要導入擴展類,實例化並傳入 Flask 程序實例:sql
from flask_sqlalchemy import SQLAlchemy # 導入擴展類
app = Flask(__name__)
db = SQLAlchemy(app) # 初始化擴展,傳入程序實例 app複製代碼
爲了設置 Flask、擴展或是咱們程序自己的一些行爲,咱們須要設置和定義一些配置變量。Flask 提供了一個統一的接口來寫入和獲取這些配置變量:Flask.config
字典。配置變量的名稱必須使用大寫,寫入配置的語句通常會放到擴展類實例化語句以前。shell
下面寫入了一個 SQLALCHEMY_DATABASE_URI
變量來告訴 SQLAlchemy 數據庫鏈接地址:數據庫
import os
# ...
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + os.path.join(app.root_path, 'data.db')複製代碼
注意 這個配置變量的最後一個單詞是 URI,而不是 URL。flask
對於這個變量值,不一樣的 DBMS 有不一樣的格式,對於 SQLite 來講,這個值的格式以下:服務器
sqlite:////數據庫文件的絕對地址複製代碼
數據庫文件通常放到項目根目錄便可,app.root_path
返回程序實例所在模塊的路徑(目前來講,即項目根目錄),咱們使用它來構建文件路徑。數據庫文件的名稱和後綴你能夠自由定義,通常會使用 .db、.sqlite 和 .sqlite3 做爲後綴。session
另外,若是你使用 Windows 系統,上面的 URI 前綴部分須要寫入三個斜線(即 sqlite:///
)。在本書的示例程序代碼裏,作了一些兼容性處理,另外還新設置了一個配置變量,實際的代碼以下:
import os
import sys
from flask import Flask
WIN = sys.platform.startswith('win')
if WIN: # 若是是 Windows 系統,使用三個斜線
prefix = 'sqlite:///'
else: # 不然使用四個斜線
prefix = 'sqlite:////'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(app.root_path, 'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 關閉對模型修改的監控複製代碼
若是你固定在某一個操做系統上進行開發,部署時也使用相同的操做系統,那麼能夠不用這麼作,直接根據你的須要寫出前綴便可。
提示 你能夠訪問 Flask 文檔的配置頁面查看 Flask 內置的配置變量;一樣的,在 Flask-SQLAlchemy 文檔的配置頁面能夠看到 Flask-SQLAlchemy 提供的配置變量。
在 Watchlist 程序裏,目前咱們有兩類數據要保存:用戶信息和電影條目信息。下面分別建立了兩個模型類來表示這兩張表:
app.py:建立數據庫模型
class User(db.Model): # 表名將會是 user(自動生成,小寫處理)
id = db.Column(db.Integer, primary_key=True) # 主鍵
name = db.Column(db.String(20)) # 名字
class Movie(db.Model): # 表名將會是 movie
id = db.Column(db.Integer, primary_key=True) # 主鍵
title = db.Column(db.String(60)) # 電影標題
year = db.Column(db.String(4)) # 電影年份複製代碼
模型類的編寫有一些限制:
db.Model
。db.Column
,傳入的參數爲字段的類型,下面的表格列出了經常使用的字段類。db.Column()
中添加額外的選項(參數)能夠對字段進行設置。好比,primary_key
設置當前字段是否爲主鍵。除此以外,經常使用的選項還有 nullable
(布爾值,是否容許爲空值)、index
(布爾值,是否設置索引)、unique
(布爾值,是否容許重複值)、default
(設置默認值)等。經常使用的字段類型以下表所示:
db.Integer
整型db.String (size)
字符串,size 爲最大長度,好比db.String(20)
db.Text
長文本db.DateTime
時間日期,Pythondatetime
對象db.Float
浮點數db.Boolean
布爾值模型類建立後,還不能對數據庫進行操做,由於咱們尚未建立表和數據庫文件。下面在 Python Shell 中建立了它們:
$ flask shell
>>> from app import db
>>> db.create_all()複製代碼
打開文件管理器,你會發現項目根目錄下出現了新建立的數據庫文件 data.db。這個文件不須要提交到 Git 倉庫,咱們在 .gitignore 文件最後添加一行新規則:
*.db複製代碼
若是你改動了模型類,想從新生成表模式,那麼須要先使用 db.drop_all()
刪除表,而後從新建立:
>>> db.drop_all()
>>> db.create_all()複製代碼
注意這會一併刪除全部數據,若是你想在不破壞數據庫內的數據的前提下變動表的結構,須要使用數據庫遷移工具,好比集成了 Alembic 的 Flask-Migrate 擴展。
提示 上面打開 Python Shell 使用的是 flask shell
命令,而不是 python
。使用這個命令啓動的 Python Shell 激活了「程序上下文」,它包含一些特殊變量,這對於某些操做是必須的(好比上面的 db.create_all()
調用)。請記住,後續的 Python Shell 都會使用這個命令打開。
和 flask shell
相似,咱們能夠編寫一個自定義命令來自動執行建立數據庫表操做:
import click
@app.cli.command() # 註冊爲命令
@click.option('--drop', is_flag=True, help='Create after drop.') # 設置選項
def initdb(drop):
"""Initialize the database."""
if drop: # 判斷是否輸入了選項
db.drop_all()
db.create_all()
click.echo('Initialized database.') # 輸出提示信息複製代碼
默認狀況下,函數名稱就是命令的名字,如今執行 flask initdb
命令就能夠建立數據庫表:
$ flask initdb複製代碼
使用 --drop
選項能夠刪除表後從新建立:
$ flask initdb --drop複製代碼
在前面打開的 Python Shell 裏,咱們來測試一下常見的數據庫操做。你能夠跟着示例代碼來操做,也能夠自由練習。
下面的操做演示瞭如何向數據庫中添加記錄:
>>> from app import User, Movie # 導入模型類
>>> user = User(name='Grey Li') # 建立一個 User 記錄
>>> m1 = Movie(title='Leon', year='1994') # 建立一個 Movie 記錄
>>> m2 = Movie(title='Mahjong', year='1996') # 再建立一個 Movie 記錄
>>> db.session.add(user) # 把新建立的記錄添加到數據庫會話
>>> db.session.add(m1)
>>> db.session.add(m2)
>>> db.session.commit() # 提交數據庫會話,只須要在最後調用一次便可複製代碼
提示 在實例化模型類的時候,咱們並無傳入 id
字段(主鍵),由於 SQLAlchemy 會自動處理這個字段。
最後一行 db.session.commit()
很重要,只有調用了這一行纔會真正把記錄提交進數據庫,前面的 db.session.add()
調用是將改動添加進數據庫會話(一個臨時區域)中。
經過對模型類的 query
屬性調用可選的過濾方法和查詢方法,咱們就能夠獲取到對應的單個或多個記錄(記錄以模型類實例的形式表示)。查詢語句的格式以下:
<模型類>.query.<過濾方法(可選)>.<查詢方法>複製代碼
下面是一些經常使用的過濾方法:
filter()
使用指定的規則過濾記錄,返回新產生的查詢對象filter_by()
使用指定規則過濾記錄(以關鍵字表達式的形式),返回新產生的查詢對象order_by()
根據指定條件對記錄進行排序,返回新產生的查詢對象group_by()
根據指定條件對記錄進行分組,返回新產生的查詢對象下面是一些經常使用的查詢方法:
all()
返回包含全部查詢記錄的列表first()
返回查詢的第一條記錄,若是未找到,則返回Noneget(id)
傳入主鍵值做爲參數,返回指定主鍵值的記錄,若是未找到,則返回Nonecount()
返回查詢結果的數量first_or_404()
返回查詢的第一條記錄,若是未找到,則返回404錯誤響應get_or_404(id)
傳入主鍵值做爲參數,返回指定主鍵值的記錄,若是未找到,則返回404錯誤響應paginate()
返回一個Pagination對象,能夠對記錄進行分頁處理下面的操做演示瞭如何從數據庫中讀取記錄,並進行簡單的查詢:
>>> from app import Movie # 導入模型類
>>> movie = Movie.query.first() # 獲取 Movie 模型的第一個記錄(返回模型類實例)
>>> movie.title # 對返回的模型類實例調用屬性便可獲取記錄的各字段數據
'Leon'
>>> movie.year
'1994'
>>> Movie.query.all() # 獲取 Movie 模型的全部記錄,返回包含多個模型類實例的列表
[<Movie 1>, <Movie 2>]
>>> Movie.query.count() # 獲取 Movie 模型全部記錄的數量
2
>>> Movie.query.get(1) # 獲取主鍵值爲 1 的記錄
<Movie 1>
>>> Movie.query.filter_by(title='Mahjong').first() # 獲取 title 字段值爲 Mahjong 的記錄
<Movie 2>
>>> Movie.query.filter(Movie.title=='Mahjong').first() # 等同於上面的查詢,但使用不一樣的過濾方法
<Movie 2>複製代碼
提示 咱們在說 Movie 模型的時候,實際指的是數據庫中的 movie 表。表的實際名稱是模型類的小寫形式(自動生成),若是你想本身指定表名,能夠定義 __tablename__
屬性。
對於最基礎的 filter()
過濾方法,SQLAlchemy 支持豐富的查詢操做符,具體能夠訪問文檔相關頁面查看。除此以外,還有更多的查詢方法、過濾方法和數據庫函數可使用,具體能夠訪問文檔的 Query API 部分查看。
下面的操做更新了 Movie
模型中主鍵爲 2
的記錄:
>>> movie = Movie.query.get(2)
>>> movie.title = 'WALL-E' # 直接對實例屬性賦予新的值便可
>>> movie.year = '2008'
>>> db.session.commit() # 注意仍然須要調用這一行來提交改動複製代碼
下面的操做刪除了 Movie
模型中主鍵爲 1
的記錄:
>>> movie = Movie.query.get(1)
>>> db.session.delete(movie) # 使用 db.session.delete() 方法刪除記錄,傳入模型實例
>>> db.session.commit() # 提交改動複製代碼
通過上面的一番練習,咱們能夠在 Watchlist 裏進行實際的數據庫操做了。
由於設置了數據庫,負責顯示主頁的 index
能夠從數據庫裏讀取真實的數據:
@app.route('/')
def index():
user = User.query.first() # 讀取用戶記錄
movies = Movie.query.all() # 讀取全部電影記錄
return render_template('index.html', user=user, movies=movies)複製代碼
在 index
視圖中,原來傳入模板的 name
變量被 user
實例取代,模板 index.html 中的兩處 name
變量也要相應的更新爲 user.name
屬性:
{{ user.name }}'s Watchlist複製代碼
由於有了數據庫,咱們能夠編寫一個命令函數把虛擬數據添加到數據庫裏。下面是用來生成虛擬數據的命令函數:
import click
@app.cli.command()
def forge():
"""Generate fake data."""
db.create_all()
# 全局的兩個變量移動到這個函數內
name = 'Grey Li'
movies = [
{'title': 'My Neighbor Totoro', 'year': '1988'},
{'title': 'Dead Poets Society', 'year': '1989'},
{'title': 'A Perfect World', 'year': '1993'},
{'title': 'Leon', 'year': '1994'},
{'title': 'Mahjong', 'year': '1996'},
{'title': 'Swallowtail Butterfly', 'year': '1996'},
{'title': 'King of Comedy', 'year': '1999'},
{'title': 'Devils on the Doorstep', 'year': '1999'},
{'title': 'WALL-E', 'year': '2008'},
{'title': 'The Pork of Music', 'year': '2012'},
]
user = User(name=name)
db.session.add(user)
for m in movies:
movie = Movie(title=m['title'], year=m['year'])
db.session.add(movie)
db.session.commit()
click.echo('Done.')複製代碼
如今執行 flask forge
命令就會把全部虛擬數據添加到數據庫裏:
$ flask forge複製代碼
本章咱們學習了使用 SQLAlchemy 操做數據庫,後面你會慢慢熟悉相關的操做。結束前,讓咱們提交代碼:
$ git add .
$ git commit -m "Add database support with Flask-SQLAlchemy"
$ git push複製代碼
提示 你能夠在 GitHub 上查看本書示例程序的對應 commit:7f167d2。