第五章 數(shù)據(jù)庫
序:什么是數(shù)據(jù)庫
數(shù)據(jù)庫按規(guī)則保存程序數(shù)據(jù),
程序發(fā)起查詢?nèi)』財?shù)據(jù)。
Web 程序最常使用基于關(guān)系模型的數(shù)據(jù)庫,
這種數(shù)據(jù)庫也稱為 SQL 數(shù)據(jù)庫,
因為它們使用結(jié)構(gòu)化查詢語言。
不過最近幾年文檔數(shù)據(jù)庫和鍵值對數(shù)據(jù)庫成了流行的替代選擇,
這兩種數(shù)據(jù)庫合稱 NoSQL數(shù)據(jù)庫。
5.1 SQL數(shù)據(jù)庫
關(guān)系型數(shù)據(jù)庫把數(shù)據(jù)存儲在表中,
表模擬程序中不同的實體。
例如,訂單管理程序的數(shù)據(jù)庫中可能有表 customers、 products 和 orders。
表的列數(shù)是固定的, 行數(shù)是可變的。
列定義表所表示的實體的數(shù)據(jù)屬性。
例如, customers表中可能有 name、 address、 phone 等列。
表中的行定義各列對應(yīng)的真實數(shù)據(jù)。
表中有個特殊的列, 稱為主鍵,
其值為表中各行的唯一標(biāo)識符。
表中還可以有稱為外鍵的列,
引用同一個表或不同表中某行的主鍵。
行之間的這種聯(lián)系稱為關(guān)系,
這是關(guān)系型數(shù)據(jù)庫模型的基礎(chǔ)。
5.2 NoSQL數(shù)據(jù)庫
所有不遵循上節(jié)所述的關(guān)系模型的數(shù)據(jù)庫統(tǒng)稱為 NoSQL 數(shù)據(jù)庫。
NoSQL 數(shù)據(jù)庫一般使用集合代替表,
使用文檔代替記錄。
NoSQL 數(shù)據(jù)庫采用的設(shè)計方式使聯(lián)結(jié)變得困難,
所以大多數(shù)數(shù)據(jù)庫根本不支持這種操作。
5.3 使用SQL還是NoSQL
SQL 數(shù)據(jù)庫擅于用高效且緊湊的形式存儲結(jié)構(gòu)化數(shù)據(jù)。
這種數(shù)據(jù)庫需要花費大量精力保證數(shù)據(jù)的一致性。
NoSQL 數(shù)據(jù)庫放寬了對這種一致性的要求,
從而獲得性能上的優(yōu)勢。
對不同類型數(shù)據(jù)庫的全面分析、 對比超出了本書范疇。
對中小型程序來說,
SQL 和 NoSQL數(shù)據(jù)庫都是很好的選擇,
而且性能相當(dāng)。
5.4 python數(shù)據(jù)庫框架
可選擇的數(shù)據(jù)庫框架
大多數(shù)的數(shù)據(jù)庫引擎都有對應(yīng)的 Python 包,
包括開源包和商業(yè)包。
Flask 并不限制你使用何種類型的數(shù)據(jù)庫包,
因此可以根據(jù)自己的喜好選擇使用
MySQL、 Postgres、 SQLite、
Redis、 MongoDB 或者 CouchDB。
如果這些都無法滿足需求,
還有一些數(shù)據(jù)庫抽象層代碼包供選擇,
例如 SQLAlchemy 和MongoEngine。
你可以使用這些抽象包直接處理高等級的 Python 對象,
而不用處理如表、文檔或查詢語言此類的數(shù)據(jù)庫實體。
選擇時需要考慮的因素
易用性
如果直接比較數(shù)據(jù)庫引擎和數(shù)據(jù)庫抽象層,顯然后者取勝。
性能
一般情況下, ORM 和 ODM 對生產(chǎn)率的提
升遠(yuǎn)遠(yuǎn)超過把對象業(yè)務(wù)轉(zhuǎn)換成數(shù)據(jù)庫業(yè)務(wù)的損耗。
真正的關(guān)鍵點在于如何選擇一個能直接操作低層數(shù)據(jù)庫的抽象層,
以防特定的操作需要直接使用數(shù)據(jù)庫原生指令優(yōu)化。
可移稙性
選擇數(shù)據(jù)庫時,必須考慮其是否能在你的開發(fā)平臺和生產(chǎn)平臺中使用。
SQLAlchemy ORM 就是一個很好的例子,
它支持很多關(guān)系型數(shù)據(jù)庫引擎,
包括流行的 MySQL、 Postgres 和 SQLite。
Flask集成度
專門為Flask 開發(fā)的擴展是你的首選,
選擇這些框架可以節(jié)省你編寫集成代碼的時間。
5.5 使用Flask-SQLAlchemy管理數(shù)據(jù)庫
什么是Flask-SQLAlchemy
Flask-SQLAlchemy 是一個 Flask 擴展,
簡化了在 Flask 程序中使用 SQLAlchemy 的操作。
SQLAlchemy 是一個很強大的關(guān)系型數(shù)據(jù)庫框架,
支持多種數(shù)據(jù)庫后臺。
SQLAlchemy 提供了高層 ORM,
也提供了使用數(shù)據(jù)庫原生 SQL 的低層功能。
如何安裝
$ pip install flask-sqlalchemy
數(shù)據(jù)庫URL
表5-1 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/to/database |
SQLite( Windows) | sqlite:///c:/absolute/path/to/database |
配置數(shù)據(jù)庫
from flask_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)
__file__
是當(dāng)前文件的文件路徑(包含文件名),
dirname可以提取出不包含文件名的路徑,
abspath可以把路徑轉(zhuǎn)換為絕對路徑,
join可以把兩個路徑合并為一個路徑。
db對象是 SQLAlchemy類的實例,
表示程序使用的數(shù)據(jù)庫,
同時還獲得了 Flask-SQLAlchemy提供的所有功能。
配置對象中還有一個很有用的選項,
即SQLALCHEMY_COMMIT_ON_TEARDOWN鍵,
將其設(shè)為 True時,
每次請求結(jié)束后都會自動提交數(shù)據(jù)庫中的變動。
5.6 定義模型
什么是模型
模型這個術(shù)語表示程序使用的持久化實體。
在 ORM 中,模型一般是一個 Python 類,
類中的屬性對應(yīng)數(shù)據(jù)庫表中的列。
Flask-SQLAlchemy 創(chuàng)建的數(shù)據(jù)庫實例
為模型提供了一個基類以及一系列輔助類和輔助函數(shù),
可用于定義模型的結(jié)構(gòu)。
示例5-2 hello.py:定義Role和User模型
class Role(db.Model):
__tabname__ = '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):
__tabname__ = '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ù)庫中使用的表名。
如果沒有定義tablename,
Flask-SQLAlchemy 會使用一個默認(rèn)名字,
但默認(rèn)的表名沒有遵守使用復(fù)數(shù)形式進(jìn)行命名的約定,
所以最好由我們自己來指定表名。
其余的類變量都是該模型的屬性,
被定義為 db.Column類的實例。
表5-2 最常用的SQLAlchemy列類型
類型名 | 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.timedelta | 時間間隔 |
Enum | str | 一組字符串 |
PickleType | 任何 Python 對象 | 自動使用 Pickle 序列化 |
LargeBinary | str | 二進(jìn)制文件 |
5-3 最常用的SQLAlchemy列選項
選項名 | 說明 |
---|---|
primary_key | 如果設(shè)為 True,這列就是表的主鍵 |
unique | 如果設(shè)為 True,這列不允許出現(xiàn)重復(fù)的值 |
index | 如果設(shè)為 True,為這列創(chuàng)建索引,提升查詢效率 |
nullable | 如果設(shè)為 True,這列允許使用空值;如果設(shè)為 False,這列不允許使用空值 |
default | 為這列定義默認(rèn)值 |
5.7 關(guān)系
一對多的關(guān)系
角色到用戶是一對多關(guān)系,
因為一個角色可屬于多個用戶,
而每個用戶都只能有一個角色。
示例5-3:一對多的代碼
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
class Role(db.Model):
# ...
users = db.relationship('User', backref='role')
User中的 role_id 列被定義為外鍵(db.ForeignKey),
它的參數(shù) 'roles.id' 表明,
這列等于__tabname__
為roles的id列。
對于一個 Role 類的實例,
其 users 屬性將返回與角色相關(guān)聯(lián)的用戶組成的列表。
也就是說,如果當(dāng)前類A的實例是a,
另一個和A相關(guān)的的類是B,
則返回所有和a相關(guān)的B的實例b。
db.relationship() 的第一個參數(shù)表明這個關(guān)系的另一端是哪個模型,
也就是B。
如果模型類尚未定義,
可使用字符串形式指定。
backref 參數(shù)向 B類中添加一個屬性,
從而定義反向關(guān)系。
這一屬性可替代外鍵訪問B類,
此時獲取的是B的實例列表,而不是外鍵的值。
大多數(shù)情況下,
db.relationship() 都能自行找到關(guān)系中的外鍵,
但有時卻無法決定把哪一列作為外鍵。 例如,
如果 User 模型中有兩個或以上的列定義為 Role 模型的外鍵,
SQLAlchemy 就不知道該使用哪列。
如果無法決定外鍵,
你就要為 db.relationship() 提供額外參數(shù),
從而確定所用外鍵。
表5-4 常用的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è)為 Fales,不使用列表,而使用標(biāo)量值 |
order_by | 指定關(guān)系中記錄的排序方式 |
secondary | 指定多對多關(guān)系中關(guān)系表的名字 |
secondaryjoin | SQLAlchemy 無法自行決定時,指定多對多關(guān)系中的二級聯(lián)結(jié)條件 |
一對一關(guān)系可以用前面介紹的一對多關(guān)系表示,
但調(diào)用 db.relationship() 時要把 uselist 設(shè)為 False,
把“多”變成“一”。
多對一關(guān)系也可使用一對多表示,
對調(diào)兩個表即可,
或者把外鍵和 db.relationship() 都放在“多”這一側(cè)。
最復(fù)雜的關(guān)系類型是多對多,
需要用到第三張表, 這個表稱為關(guān)系表。
你將在第 12 章學(xué)習(xí)多對多關(guān)系。
5.8 數(shù)據(jù)庫操作
5.8.1 創(chuàng)建表
>>> from hello import db
>>> db.create_all()
>>> db.drop_all()
5.8.2 插入行
>>> 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)
這些新建對象的 id屬性并沒有明確設(shè)定,
因為主鍵是由 Flask-SQLAlchemy 管理的。
現(xiàn)在這些對象只存在于Python 中,
還未寫入數(shù)據(jù)庫。因此 id 尚未賦值:
>>> print(admin_role.id)
None
>>> print(mod_role.id)
None
>>> print(user_role.id)
None
通過數(shù)據(jù)庫會話管理對數(shù)據(jù)庫所做的改動,
在 Flask-SQLAlchemy 中,會話由 db.session表示。
準(zhǔn)備把對象寫入數(shù)據(jù)庫之前,
先要將其添加到會話中:
數(shù)據(jù)庫會話 db.session 和第 4 章介紹的 Flasksession 對象沒有關(guān)系。
數(shù)據(jù)庫會話也稱為事務(wù)。
>>> 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)
或者簡寫成:
>>> db.session.add_all([admin_role, mod_role, user_role,
... user_john, user_susan, user_david])
為了把對象寫入數(shù)據(jù)庫,
我們要調(diào)用commit()方法提交會話:
db.session.commit()
再次查看 id 屬性,現(xiàn)在它們已經(jīng)賦值了:
>>> print(admin_role.id)
1
>>> print(mod_role.id)
2
>>> print(user_role.id)
3
如果在寫入會話的過程中發(fā)生了錯誤,
整個會話都會失效。
如果你始終把相關(guān)改動放在會話中提交,
就能避免因部分更新導(dǎo)致的數(shù)據(jù)庫不一致性。
數(shù)據(jù)庫會話也可回滾。
調(diào)用 db.session.rollback() 后,
添加到數(shù)據(jù)庫會話中的所有對象
都會還原到它們在數(shù)據(jù)庫時的狀態(tài)。
5.8.3 修改行
在數(shù)據(jù)庫會話上調(diào)用 add() 方法也能更新模型。
下面這個例子把 "Admin" 角色重命名為 "Administrator":
>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()
5.8.4 刪除行
數(shù)據(jù)庫會話還有個 delete() 方法。
下面這個例子把 "Moderator" 角色從數(shù)據(jù)庫中刪除:
>>> db.session.delete(mod_role)
>>> db.session.commit()
注意,刪除與插入和更新一樣,
提交數(shù)據(jù)庫會話后才會執(zhí)行。
5.8.5 查詢行
Flask-SQLAlchemy 為每個模型類都提供了 query 對象。
最基本的模型查詢是取回對應(yīng)表中的所有記錄:
>>> Role.query.all()
[<Role u'Administrator'>, <Role u'User'>]
>>> User.query.all()
[<User u'john'>, <User u'susan'>, <User u'david'>]
使用過濾器可以配置 query 對象進(jìn)行更精確的數(shù)據(jù)庫查詢。
下面這個例子查找角色為"User" 的所有用戶:
>>> User.query.filter_by(role=user_role).all()
[<User u'susan'>, <User u'david'>]
若要查看 SQLAlchemy 為查詢生成的原生 SQL 查詢語句,
只需把 query 對象轉(zhuǎn)換成字符串:
>>> 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 會話,
前面這些例子中創(chuàng)建的對象就不會以 Python 對象的形式存在,
而是作為各自數(shù)據(jù)庫表中的行。
下面這個例子發(fā)起了一個查詢,
加載名為 "User" 的用戶角色:
>>> user_role = Role.query.filter_by(name='User').first()
filter_by() 等過濾器在 query 對象上調(diào)用,
返回一個更精確的 query 對象。
多個過濾器可以一起調(diào)用,直到獲得所需結(jié)果。
表5-5 常用的SQLAlchemy查詢過濾器
過濾器 | 說明 |
---|---|
filter() | 把過濾器添加到原查詢上,返回一個新查詢 |
filter_by() | 把等值過濾器添加到原查詢上,返回一個新查詢 |
limit() | 使用指定的值限制原查詢返回的結(jié)果數(shù)量,返回一個新查詢 |
offset() | 偏移原查詢返回的結(jié)果,返回一個新查詢 |
order_by() | 根據(jù)指定條件對原查詢結(jié)果進(jìn)行排序,返回一個新查詢 |
group_by() | 根據(jù)指定條件對原查詢結(jié)果進(jìn)行分組,返回一個新查詢 |
在查詢上應(yīng)用指定的過濾器后,
通過調(diào)用 all() 執(zhí)行查詢,
以列表的形式返回結(jié)果。
除了all() 之外,還有其他方法能觸發(fā)查詢執(zhí)行。
表 5-6 列出了執(zhí)行查詢的其他方法。
表5-6 常用的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)的行,如果沒找到指定的主鍵,則終止請求,返回 404 錯誤響應(yīng) |
count() | 返回查詢結(jié)果的數(shù)量 |
paginate() | 返回一個 Paginate 對象,它包含指定范圍內(nèi)的結(jié)果 |
>>> users = user_role.users
>>> users
[<User u'susan'>, <User u'david'>]
>>> users[0].role
<Role u'User'>
這個例子中的 user_role.users 查詢有個小問題。
執(zhí)行 user_role.users 表達(dá)式時,
隱含的查詢會調(diào)用 all() 返回一個用戶列表。
query 對象是隱藏的,
因此無法指定更精確的查詢過濾器。
在示例5-4 中,我們修改了關(guān)系的設(shè)置,
加入了 lazy = 'dynamic' 參數(shù),
從而禁止自動執(zhí)行查詢。
示例5-4 hello.py:動態(tài)關(guān)系
class Role(db.Model):
# ...
users = db.relationship('User', backref='role', lazy='dynamic')
# ...
這樣配置關(guān)系之后,
user_role.users 會返回一個尚未執(zhí)行的查詢,
因此可以在其上添加過濾器:
>>> user_role.users.order_by(User.username).all()
[<User u'david'>, <User u'susan'>]
>>> user_role.users.count()
2
5.9 在視圖函數(shù)中操作數(shù)據(jù)庫
數(shù)據(jù)庫操作可以直接在視圖函數(shù)中進(jìn)行,
示例 5-5 展示了首頁路由的新版本,
已經(jīng)把用戶輸入的名字寫入了數(shù)據(jù)庫。
@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 reder_template('index.html',
form = form, name = session.get('name'),
known = session.get('known', False))
在這個修改后的版本中,
提交表單后,
程序會使用 filter_by() 查詢過濾器在數(shù)據(jù)庫中查找提交的名字。
變量 known 被寫入用戶會話中,
因此重定向之后,可以把數(shù)據(jù)傳給模板,
用來顯示自定義的歡迎消息。
示例5-6 templates/index.html
{% 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 %}
5.10 集成Python shell
次啟動 shell 會話都要導(dǎo)入數(shù)據(jù)庫實例和模型,
這真是份枯燥的工作。
為了避免一直重復(fù)導(dǎo)入,
我們可以做些配置,
讓 Flask-Script 的 shell 命令自動導(dǎo)入特定的對象。
若想把對象添加到導(dǎo)入列表中,
我們要為 shell 命令注冊一個 make_context 回調(diào)函數(shù),
如示例 5-7 所示。
示例 5-7 hello.py: 為 shell 命令添加一個上下文
from flask_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() 函數(shù)注冊了程序、數(shù)據(jù)庫實例以及模型,
因此這些對象能直接導(dǎo)入 shell:
$ python hello.py shell
>>> app
<Flask 'app'>
>>> db
<SQLAlchemy engine='sqlite:////home/flask/flasky/data.sqlite'>
>>> User
<class 'app.User'>
5.11 使用alembic數(shù)據(jù)庫遷移
安裝
Alembic是SQLAlchemy作者編寫的Python數(shù)據(jù)庫遷移工具。
通過pip安裝,會自動安裝依賴包SQLAlchemy、Mako和MarkupSafe。
pip install alembic
安裝完成后就可以使用alembic命令,
所有Alembic操作均由該命令實現(xiàn)(類似Git)。
初始化
cd到你的應(yīng)用程序的路徑,
然后在命令行輸入:
alembic init alembic
第一個alembic是聲明要使用alembic的命令,
第二個alembic可以隨便命名,是用來存放數(shù)據(jù)庫環(huán)境配置文件的。
輸入命令后,數(shù)據(jù)庫環(huán)境創(chuàng)建完成。
這時在目錄下出現(xiàn)了一個alembic的文件夾。
在進(jìn)行初始化時,
根據(jù)使用多數(shù)據(jù)庫、單數(shù)據(jù)庫或者pylons,
選擇不同的選項,默認(rèn)是單數(shù)據(jù)庫。
如果要使用pylons項目,可以使用下列命令:
alembic init --template pylons ./scripts
配置數(shù)據(jù)庫地址
在alembic的同級目錄下,
同樣生成了一個alembic.ini配置文件。
當(dāng)alembic腳本激活時,
會查找這個配置文件的選項并應(yīng)用。
也可以利用--config選項來指定.ini文件的位置。
在初始化結(jié)束后,需要配置數(shù)據(jù)庫連接URL,
在alembic.ini中設(shè)置:
sqlalchemy.url = sqlite:///data.sqlite
sqlite:////是絕對地址,sqlite:///是相對地址。
配置自動遷移
在alembic文件夾的env.py中,
有一個選項是target_metadata = None。
修改這個選項可以自動生成遷移腳本,
我們將它修改為:
from app import db
target_metadata = db.metadata
但運行腳本時會提示還不知道app模塊的位置。
我們在這兩行代碼前,把app的位置添加到系統(tǒng)環(huán)境變量。
import os
import sys
root = os.path.dirname(__file__) + '/../'
print(root)
sys.path.append(root)
現(xiàn)在就可以使用自動遷移選項了。
自動遷移檢查哪些數(shù)據(jù)庫變化
自動遷移會檢查
- Table的增加、刪除
- Column的增加、刪除
- Column的空狀態(tài)變化
- Index的基本變化和明確命名的UniqueConstraint 0.6.1增加
- ForeignKey constraint的基本變化 0.7.1增加
可選擇性的檢查
- Column類型的變化
- Server default的更改
要檢測Column類型的變化,
需要把python包中的alembic/runtime/evironment.py中的
configure函數(shù)的compare_type設(shè)為True。
不會檢查
- Tablename的改變
- Column name的改變
- 匿名constraints
- 特殊的SQLAlchemy類型,例如Enum在不支持的引擎中使用
創(chuàng)建版本
用alembic revision --autogenerate -m "注釋"來創(chuàng)建數(shù)據(jù)庫版本。
自動遷移和手動遷移的區(qū)別在于,
是否自動在遷移腳本中提供upgrade和downgrade函數(shù)的功能,
這兩個函數(shù)用來升級或者回滾數(shù)據(jù)庫版本。
加上--autogenerate
就是自動遷移,否則是手動遷移。
遷移腳本位于alembic文件夾下的revision文件夾中,
以header信息加注釋的形式命名。
升級和回滾
使用alembic upgrade
進(jìn)行升級,
使用alembic downgrade
進(jìn)行回滾。
例如:
$ alembic upgrade head
刪除歷史記錄
Alembic在數(shù)據(jù)庫中僅保存當(dāng)前版本號,
其余信息均從文件讀取。
刪除歷史記錄,
只需要將數(shù)據(jù)庫表刪除,
并刪除versions下的所有文件。
而alembic.ini和env.py中的設(shè)置無需更改,
可以再次使用。