大多數的數據庫引擎都有對應的 Python 包,包括開源包和商業包。Flask 並不限制你使用何種類型的數據庫包,所以能夠根據本身的喜愛選擇使用 MySQL、Postgres、SQLite、Redis、MongoDB 或者 CouchDB。html
若是這些都沒法知足需求,還有一些數據庫抽象層代碼包供選擇,例如SQLAlchemy和MongoEngine。你可使用這些抽象包直接處理高等級的 Python 對象,而不用處理如表、文檔或查詢語言此類的數據庫實體。python
選擇數據庫框架的因素:mysql
pip install flask-sqlalchemy
使用URL制定數據庫sql
數據庫引擎 | URL |
---|---|
MySQL | mysql://username:password@hostname/database |
Postgres | postgresql://username:password@hostname/database |
SQLite(Unix) | sqlite:////absolute/path/to/database |
SQLite(Windows) | sqlite:///c:/absolute/path/to/database |
SQLite 數 據 庫 不 需 要 使 用 服 務 器, 因 此 不 用 指 定 hostname 、 username 和 password 。URL 中的 database 是硬盤上文件的文件名。shell
配置對象中還有一個頗有用的選項,即 SQLALCHEMY_COMMIT_ON_TEARDOWN 鍵,將其設爲 True時,每次請求結束後都會自動提交數據庫中的變更數據庫
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)
class Role(db.Model): __tablename__ = 'roles'#__tablename__ 定義在數據庫中使用的表名 id = db.Column(db.Integer, primary_key=True)#primary_key若是設爲 True ,這列就是表的主鍵.若是沒有定義 __tablename__ ,SQLAlchemy 會使用一個默認名字 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
__repr__&__str__flask
最經常使用的SQLAlchemy列類型bootstrap
類型名 | Python類型 | 說 明 |
---|---|---|
Integer | int | 普通整數,通常是 32 位 |
SmallInteger | int | 取值範圍小的整數,通常是 16 位 |
BigInteger | int 或 long | 不限制精度的整數 |
Float | float | 浮點數 |
Numeric | decimal.Decimal | 定點數 |
String | str | 變長字符串 |
Text | str | 變長字符串,對較長或不限長度的字符串作了優化 |
Unicode | unicode | 變長 Unicode 字符串 |
UnicodeText | unicode | 變長 Unicode 字符串,對較長或不限長度的字符串作了優化 |
Boolean | bool | 布爾值 |
Date | datetime.date | 日期 |
Time | datetime.time | 時間 |
DateTime | datetime.datetime | 日期和時間 |
Interval | datetime.timedelta | 時間間隔 |
Enum | str | 一組字符串 |
PickleType | 任何 Python 對象 | 自動使用 Pickle 序列化 |
LargeBinary | str | 二進制文件 |
最常使用的SQLAlchemy列選項session
選項名 | 說 明 |
---|---|
primary_key | 若是設爲 True ,這列就是表的主鍵 |
unique | 若是設爲 True ,這列不容許出現重複的值 |
index | 若是設爲 True ,爲這列建立索引,提高查詢效率 |
nullable | 若是設爲 True ,這列容許使用空值;若是設爲 False ,這列不容許使用空值 |
default | 爲這列定義默認值 |
關係型數據庫使用關係把不一樣表中的行聯繫起來。app
class Role(db.Model): # ... users = db.relationship('User', backref='role')#添加到 Role 模型中的 users 屬性表明這個關係的面向對象視角。對於一個 Role 類的實例,其 users 屬性將返回與角色相關聯的用戶組成的列表。db.relationship() 的第一個參數表,若是模型類還沒有定義,可以使用字符串形式指定。db.relationship() 中的 backref 參數向 User 模型中添加一個 role 屬性,從而定義反向關係。這一屬性可替代 role_id 訪問 Role 模型,此時獲取的是模型對象 class User(db.Model): # ... role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))#關係使用 users 表中的外鍵鏈接了兩行。添加到 User 模型中的 role_id 列被定義爲外鍵,就是這個外鍵創建起了關係。傳給 db.ForeignKey() 的參數 'roles.id' 代表,這列的值是 roles 表中行的 id 值。
db.relationship() 都能自行找到關係中的外鍵,但有時卻沒法決定把哪一列做爲外鍵。若是 User 模型中有兩個或以上的列定義爲 Role 模型的外鍵,SQLAlchemy 就不知道該使用哪列。若是沒法決定外鍵,你就要爲 db.relationship() 提供額外參數,從而肯定所用外鍵
經常使用的SQLAlchemy關係選項
選項名 | 說 明 |
---|---|
backref | 在關係的另外一個模型中添加反向引用 |
primaryjoin | 明確指定兩個模型之間使用的聯結條件。只在模棱兩可的關係中須要指定 |
lazy | 指定如何加載相關記錄。可選值有 select (首次訪問時按需加載)、 immediate (源對象加載後就加載)、 joined (加載記錄,但使用聯結)、 subquery (當即加載,但使用子查詢),noload (永不加載)和 dynamic (不加載記錄,但提供加載記錄的查詢) |
uselist | 若是設爲 Fales ,不使用列表,而使用標量值 |
order_by | 指定關係中記錄的排序方式 |
secondary | 指定 多對多 關係中關係表的名字 |
secondaryjoin | SQLAlchemy 沒法自行決定時,指定多對多關係中的二級聯結條件 |
一對一
一對一關係能夠用前面介紹的一對多關係表示,但調用 db.relationship() 時要把 uselist 設爲 False ,把「多」變成「一」。
多對多
tags = db.Table('tags', db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')), db.Column('page_id', db.Integer, db.ForeignKey('page.id')) ) class Page(db.Model): id = db.Column(db.Integer, primary_key=True) tags = db.relationship('Tag', secondary=tags, backref=db.backref('pages', lazy='dynamic')) class Tag(db.Model): id = db.Column(db.Integer, primary_key=True)
python hello.py shell
>>> from hello import db >>> db.create_all()
db.drop_all()
#建立對象,模型的構造函數接受的參數是使用關鍵字參數指定的模型屬性初始值。 admin_role = Role(name='Admin') user_role = Role(name='User') user_susan = User(username='susan', role=user_role)#role 屬性也可以使用,雖然它不是真正的數據庫列,但倒是一對多關係的高級表示。 user_john = User(username='john', role=admin_role) #這些新建對象的 id 屬性並無明確設定,由於主鍵是由 Flask-SQLAlchemy 管理的。 print(admin_role.id)#None #經過數據庫會話管理對數據庫所作的改動,在 Flask-SQLAlchemy 中,會話由 db.session 表示。 ##首先,將對象添加到會話中 db.session.add(admin_role) db.session.add(user_role) db.session.add(user_susan) db.session.add(user_john) #簡寫:db.session.add_all([admin_role, user_role, user_john, user_susan]) ##經過提交會話(事務),將對象寫入數據庫 db.session.commit()
會話提交:
數據庫會話能保證數據庫的一致性。提交操做使用原子方式把會話中的對象所有寫入數據庫。若是在寫入會話的過程當中發生了錯誤,整個會話都會失效。
數據庫會話也可 回滾 。調用 db.session.rollback() 後,添加到數據庫會話中的全部對象都會還原到它們在數據庫時的狀態。
admin_role.name = 'Administrator' db.session.add(admin_role) session.commit()
db.session.delete(mod_role) session.commit()
查詢行
user_role = Role.query.filter_by(name='User').first()#filter_by() 等過濾器在 query 對象上調用,返回一個更精確的 query 對象。
經常使用過濾器
過濾器 | 說 明 |
---|---|
filter() | 把過濾器添加到原查詢上,返回一個新查詢 |
filter_by() | 把等值過濾器添加到原查詢上,返回一個新查詢 |
limit() | 使用指定的值限制原查詢返回的結果數量,返回一個新查詢 |
offset() | 偏移原查詢返回的結果,返回一個新查詢 |
order_by() | 根據指定條件對原查詢結果進行排序,返回一個新查詢 |
group_by() | 根據指定條件對原查詢結果進行分組,返回一個新查詢 |
最常使用的SQLAlchemy查詢執行函數
方 法 | 說 明 |
---|---|
all() | 以列表形式返回查詢的全部結果 |
first() | 返回查詢的第一個結果,若是沒有結果,則返回 None |
first_or_404() | 返回查詢的第一個結果,若是沒有結果,則終止請求,返回 404 錯誤響應 |
get() | 返回指定主鍵對應的行,若是沒有對應的行,則返回 None |
get_or_404() | 返回指定主鍵對應的行,若是沒找到指定的主鍵,則終止請求,返回 404 錯誤響應 |
count() | 返回查詢結果的數量 |
paginate() | 返回一個 Paginate 對象,它包含指定範圍內的結果 |
關係查詢
#執行 user_role.users 表達式時,隱含的查詢會調用 all() 返回一個用戶列表。 query 對象是隱藏的,所以沒法指定更精確的查詢過濾器。 users = user_role.users #修改了關係的設置,加入了 lazy = 'dynamic' 參數,從而禁止自動執行查詢 class Role(db.Model): users = db.relationship('User', backref='role', lazy='dynamic') #順序排列 user_role.users.order_by(User.username).all()
@app.route('/', methods=['GET', 'POST']) def index(): form = NameForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.name.data).first() if user is None: user = User(username = form.name.data) db.session.add(user) session['known'] = False else: session['known'] = True session['name'] = form.name.data form.name.data = '' return redirect(url_for('index')) return render_template('index.html', form = form, name = session.get('name'), known = session.get('known', False))
修改模板
{ % extends "base.html" % }
{ % import "bootstrap/wtf.html" as wtf % }
{ % block title % }Flasky{ % endblock % }
{ % block page_content % }
<div class="page-header"> <h1>Hello, { % if name % }{{ name }}{ % else % }Stranger{ % endif % }!</h1> { % if not known % } <p>Pleased to meet you!</p> { % else % } <p>Happy to see you again!</p> { % endif % } </div> {{ wtf.quick_form(form) }} { % endblock % }
讓 Flask-Script 的 shell 命令自動導入特定的對象
from flask.ext.script import Shell def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context))
make_shell_context() 函數註冊了程序、數據庫實例以及模型,所以這些對象能直接導入 shell
pip install flask-migrate
from flask.ext.migrate import Migrate, MigrateCommand # ... migrate = Migrate(app, db) manager.add_command('db', MigrateCommand)
python hello.py db init
python hello.py db migrate -m "initial migration"
python hello.py db upgrade