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()