Flask-SQLAlchemy
简介与安装
SQLAlchemy是一个强大的关系型数据库框架,支持多种数据库后台。SQLAlchemy提供了高层ORM,也提供了使用数据库原生SQL的低层功能
alchemy 读作#ˈælkəmi
安装:pip install flask-sqlalchemy
FLask-SQLAlchemy数据库URL
数据库引擎 | URI | eg |
---|---|---|
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:////G:\blog\data.sqlite |
SQLite 数据库不需要使用服务器,因此不用指定 hostname、username 和password。
URL 中的 database 是硬盘上文件的文件名
SQLAlchemy常用列类型
类型名 | Python类型 | 说明 |
---|---|---|
Integer | int | 普通整数,一般是32位 |
SmallInteger | int | 取值范围小的整数,一般是16位 |
BigInteger | int或long | 不限制精度的整数 |
Float | float | 浮点数 |
Numeric | secimalDecimal | 定点数 |
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 | 一组字符串 |
PickleTrype | 任何python对象 | 自动使用Pickle序列化 |
LargeBinary | str | 二进制文件 |
SQLAlchemy常用列选项
选项名 | 说明 |
---|---|
primary_key | 如果设为 True,这列就是表的主键 |
unique | 如果设为 True,这列不允许出现重复的值 |
index | 如果设为 True,为这列创建索引,提升查询效率 |
nullable | 如果设为 True,这列允许使用空值;如果设为 False,这列不允许使用空值 |
default | 为这列定义默认值 |
SQLAlchemy常用关系
选项名 | 说明 |
---|---|
backref | 在关系的另一个模型中添加反向引用 |
primaryjoin | 明确指定两个模型之间使用的联结条件。只在模棱两可的关系中需要指定 |
lazy | 指定如何加载相关记录。可选值有 select(首次访问时按需加载)、immediate(源对象加载后就加载)、joined(加载记录,但使用联结)、subquery(立即加载,但使用子查询), noload(永不加载)和 dynamic(不加载记录,但提供加载记录的查询) |
uselist | 如果设为 Fales,不使用列表,而使用标量值 |
order_by | 指定关系中记录的排序方式 |
secondary | 指定 多对多 关系中关系表的名字 |
secondaryjoin | 指定 多对多 关系中关系表的名字 |
配置信息
必须配置 SQLALCHEMY_DATABASE_URI
建议将SQLALCHEMY_TRADE_CONDIFITION 设为False,降低内存消耗
from flask import Flask
import os
from flask_sqlalchemy import SQLAlchemy
app = Flask(__file__)
basedir = os.path.asbpath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + basedir + '\\dev-data.sqlite'
app.config['SQLALCHEMY_TRADE_CONDIFITION'] = False
创建db实例
# ...
db = SQLAlchemy(app)
定义模型
定义表名及表字段
hello.py
#...
class Role(db.model):
__tabelname__ = "roles" #表名,如果未设置,SQLAlchemy会使用一个默认名字
id = db.Column(db.Integer, primary_key=True) #行,id,主键
name = db.Column(db.String(64), unique=True) #name字段,唯一
users = db.relationship('User', backref='role') # backref定义的值,是后续创建user外键行时需要参数
def __perp__(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)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __perp__(self):
return "<User %r>', % self.username
数据库操作
生成表
不存在数据库时,会生成数据库。
如果已存在数据表,db.create_all()将不会重新创建或者更新对应的表
# flask shell 进入交互界面
from hello import db # 是在hello中定义的模型
db.create_all()
删除表
修改模型后,需要更新表,则需要删除所有表,然后再重新生成。
但是该种方式会将数据库中原有数据都清除
db.drop_all()
db.create_all()
会话(db.session)
对数据库的改动通过会话进行管理,把数据写入库之前,需要先添加到会话中
db.session.add(xxx) # 添加会话
db.session.add_all([xxx,aaa])
db.session.commit() # 将会话中的数据提交到数据库
db.session.rollback() # 添加到数据库会话中的所有对象都还原到数据库中的状态
增加行
from hello import Role, User
admin_role = Role(name='admin')
user_role = Role(username='user', role=admin_role)
db.session.add(admin_role)
db.session.add(user_role)
# 批量添加db.session.add_all([admin_role , user_role])
db.session.commit()
修改行
admin_role.name = 'administrator'
db.session.add(admin_role)
db.session.commit()
删除行
db.session.delete(user_role)
db.session.commit()
#若要重新创建user_role字段,使用db.sesson.transient(user_role)恢复,后再add\commit
查询行
>>>Role.query.all()
[<Role 'admin'>, <Role 'Moderator'>, <Role 'User'>]
Role.query.first()
<Role 'admin'>
User.query.filter_by(role=user_role).all()
[<User 'admin'>]
>>> Role.query.limit(2).all()
[<Role 'admin'>, <Role 'Moderator'>]
>>>admin_role = Role.query.filter_by(name='admin').first()
>>>admin_role.users # Role模型中,users未添加lazy=dynamic属性时,会默认自动执行all(),此时query对象隐藏起来了,无法增加精确筛选
[<User 'admin'>]
常用的查询过滤器
过滤器 | 说明 |
---|---|
filter() | 把过滤器添加到原查询上,返回一个新查询 |
filter_by() | 把等值过滤器添加到原查询上,返回一个新查询 |
limit() | 使用指定的值限制原查询返回的结果数量,返回一个新查询 |
offset() | 偏移原查询返回的结果,返回一个新查询 |
order_by() | 根据指定条件对原查询结果进行排序,返回一个新查询 |
group_by() | 根据指定条件对原查询结果进行分组,返回一个新查询 |
最常使用的查询执行函数
方法 | 说明 |
---|---|
all() | 以列表形式返回查询的所有结果 |
first() | 返回查询的第一个结果,如果没有结果,则返回 None |
first_or_404() | 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应 |
get() | 返回指定主键对应的行,如果没有对应的行,则返回 None |
get_or_404() | 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错误响应 |
count() | 返回查询结果的数量 |
paginate() | 返回一个 Paginate 对象,它包含指定范围内的结果 |
集成python shell
每次启动shell都要导入数据库实例和模型,避免重复工作,我们可以做些配置自动导入这些对象。
hello.py
# ...
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
flask shell
>>> app
<Flask 'hello'>
>>> db
数据库迁移
在开发过程中需要对数据库结构进行修改,修改完模型后,需要更新数据库。
db.create_all()在存在表时,不会重新生成表,必须db.drop_all()后重新生成,但是这样会清除历史数据。
安装与初始化
pip install flask-migrate
from flask_migrate import Migrate
# ...
migrate = Migrate(app, db)
新项目中增加数据库迁移
(venv) set FLASK_APP=hello.py
(venv) set FLASK_DEBUG=1
(venv) flask db init
更新数据库
修改模型
class User(db.model):
# ...
password = db.Column(db.String())
在flask shello环境中执行 set FLASK_APP=hello.py
- 执行flask db migrate,创建一个迁移脚本
- 检查自动生成的脚本,根据模型实际改动调整(SQLite问题处理)
- 比如重命名一列,如果使用自动迁移,会删除一列,再新增一列,导致该列数据被清空
- flask db upgrade 迁移应用到数据库中
>>> set FLASK_APP=hello.py
>>> flask db migrate
>>> flask db upgrade