flask-sqlalchemy用法详解
一. 安装
$ pip install flask-sqlalchemy
二. 配置
配置选项列表 :
选项 | 说明 |
---|---|
SQLALCHEMY_DATABASE_URI | 用于连接的数据库 URI 。例如:sqlite:////tmp/test.db 或 mysql://username:password@server/db |
SQLALCHEMY_BINDS | 一个映射 binds 到连接 URI 的字典。更多 binds 的信息见 用 Binds 操作多个数据库 。 |
SQLALCHEMY_ECHO | 如果设置为 Ture , SQLAlchemy 会记录所有 发给 stderr 的语句,这对调试有用。 |
SQLALCHEMY_RECORD_QUERIES | 可以用于显式地禁用或启用查询记录。查询记录 在调试或测试模式自动启用。更多信息见 get_debug_queries() 。 |
SQLALCHEMY_TRACE_MODIFYCATIONS=False #是否追踪对象的修改
SQLALCHEMY_NATIVE_UNICODE | 可以用于显式禁用原生 unicode 支持。当使用 不合适的指定无编码的数据库默认值时,这对于 一些数据库适配器是必须的(比如 Ubuntu 上某些版本的 PostgreSQL )。|
| SQLALCHEMY_POOL_SIZE | 数据库连接池的大小。默认是引擎默认值(通常 是 5 ) |
| SQLALCHEMY_POOL_TIMEOUT | 设定连接池的连接超时时间。默认是 10 。 |
| SQLALCHEMY_POOL_RECYCLE | 多少秒后自动回收连接。这对 MySQL 是必要的, 它默认移除闲置多于 8 小时的连接。注意如果 使用了 MySQL , Flask-SQLALchemy 自动设定这个值为 2 小时。|
- app.config["SQLALCHEMY_DATABASE_URI"] = DATABASE_URI
- app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"] = True/False # 每次请求结束后都会自动提交数据库中的变动.
- app.config[""] =
- app.config[""] =
- app.config[""] =
- app.config[""] =
- DATABASE_URI :
- mysql : mysql://username:password@hostname/database
- pgsql : postgresql://username:password@hostname/database
- sqlite(linux) : sqlite:////absolute/path/to/database
- sqlite(windows) : sqlite:///c:/absolute/path/to/database
三. 初始化示例
- from flask import Flask
- from flask_sqlalchemy import SQLAlchemy
- base_dir = os.path.abspath(os.path.dirname(__file__))
- app = Flask(__name__)
- app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite')
- app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"] = True
- db = SQLAlchemy(app)
四. 定义模型
模型 表示程序使用的持久化实体. 在 ORM 中, 模型一般是一个 Python 类, 类中的属性对应数据库中的表.
Flaks-SQLAlchemy 创建的数据库实例为模型提供了一个基类以及一些列辅助类和辅助函数, 可用于定义模型的结构.
- db.Model # 创建模型,
- db.Column # 创建模型属性.
模型属性类型 :
类型名 | Python类型 | 说明 |
---|---|---|
Integer | int | 普通整数,一般是 32 位 |
SmallInteger | int | 取值范围小的整数,一般是 16 位 |
Big Integer | int 或 long | 不限制精度的整数 |
Float | float | 浮点数 |
Numeric | decimal.Decimal | 定点数 |
String | str | 变长字符串 |
Text | str | 变长字符串,对较长或不限长度的字符串做了优化 |
Unicode | unicode | 变长 Unicode 字符串 |
Unicode Text | unicode | 变长 Unicode 字符串,对较长或不限长度的字符串做了优化 |
Boolean | bool | 布尔值 |
Date | datetime.date | 日期 |
Time | datetime.time | 时间 |
DateTime | datetime.datetime | 日期和时间 |
Interval | datetime.timedelta | 时间间隔 |
Enum | str | 一组字符串 |
PickleType | 任何 Python 对象 | 自动使用 Pickle 序列化 |
LargeBinary | str | 二进制文件 |
常用 SQLAlchemy 列选项
选项名 | 说明 |
---|---|
primary_key | 如果设为 True,这列就是表的主键 |
unique | 如果设为 True,这列不允许出现重复的值 |
index | 如果设为 True,为这列创建索引,提升查询效率 |
nullable | 如果设为 True,这列允许使用空值;如果设为 False,这列不允许使用空值 |
default | 为这列定义默认值 |
Flask-SQLAlchemy 要求每个模型都要定义主键, 这一列通常命名为 id .
示例 :
- 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 '<Role %r>' % self.username
五. 关系
关系型数据库使用关系把不同表中的行联系起来.
常用 SQLAlchemy 关系选项 :
选项名 | 说明 |
---|---|
backref | 在关系的另一个模型中添加反向引用 |
primaryjoin | 明确指定两个模型之间使用的联结条件。只在模棱两可的关系中需要指定. |
lazy | 指定如何加载相关记录。可选值如下 : |
select(首次访问时按需加载) | |
immediate(源对象加载后就加载) | |
joined(加载记录,但使用联结) | |
subquery(立即加载,但使用子查询) | |
noload(永不加载) | |
dynamic(不加载记录,但提供加载记录的查询) | |
uselist | 如果设为 Fales,不使用列表,而使用标量值 |
order_by | 指定关系中记录的排序方式 |
secondary | 指定多对多关系中关系表的名字 |
secondaryjoin | SQLAlchemy 无法自行决定时,指定多对多关系中的二级联结条件 |
1) 一对多
原理 : 在 “多” 这一侧加入一个外键, 指定 “一” 这一侧联结的记录.
示例代码 : 一个角色可属于多个用户, 而每个用户只能有一个角色.
- class Role(db.Model):
- # ...
- users = db.relationship('User', backref='role')
- class User(db.Model):
- # ...
- role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) # 外键关系.
- ###############
- db.ForeignKey('roles.id') : 外键关系,
- Role.users = db.relationship('User', backref='role') : 代表 外键关系的 面向对象视角. 对于一个 Role 类的实例, 其 users 属性将返回与角色相关联的用户组成的列表.
- db.relationship() 第一个参数表示这个关系的另一端是哪个模型.
- backref 参数, 向 User 模型添加了一个 role 数据属性, 从而定义反向关系. 这一属性可替代 role_id 访问 Role 模型, 此时获取的是模型对象, 而不是外键的值.
2) 多对多
最复杂的关系类型, 需要用到第三章表, 即 关联表 , 这样多对多关系可以分解成原表和关联表之间的两个一对多关系.
查询多对多关系分两步 : 遍历两个关系来获取查询结果.
代码示例:
- registrations = db.Table("registrations",
- db.Column("student_id", db.Integer, db.ForeignKey("students.id")),
- db.Column("class_id", db.Integer, db.ForeignKey("classes.id"))
- )
- class Student(db.Model):
- __tablename__ = "students"
- id = db.Column(db.Integer, primary_key=True)
- name = db.Column(db.String)
- classes = db.relationship("Class",
- secondary=registrations,
- backref=db.backref("students", lazy="dynamic"),
- lazy="dynamic")
- class Class(db.Model):
- __tablename__ = "classes"
- id = db.Column(db.Integer, primary_key=True)
- name = db.Column(db.String)
多对多关系仍然使用定义一对多关系的 db.relationship() 方法进行定义, 但在多对多关系中, 必须把 secondary 参数设为 关联表.
多对多关系可以在任何一个类中定义, backref 参数会处理好关系的另一侧.
关联表就是一个简单的表, 不是模型, SQLAlchemy 会自动接管这个表.
classes 关系使用列表语义, 这样处理多对多关系比较简单.
Class 模型的 students 关系有 参数 db.backref() 定义. 这个关系还指定了 lazy 参数, 所以, 关系两侧返回的查询都可接受额外的过滤器.
自引用关系
自引用关系可以理解为 多对多关系的特殊形式 : 多对多关系的两边由两个实体变为 一个实体.
高级多对多关系
使用多对多关系时, 往往需要存储所联两个实体之间的额外信息. 这种信息只能存储在关联表中. 对用户之间的关注来说, 可以存储用户关注另一个用户的日期, 这样就能按照时间顺序列出所有关注者.
为了能在关系中处理自定义的数据, 必须提升关联表的地位, 使其变成程序可访问的模型.
关注关联表模型实现:
- class Follow(db.Model):
- __tablename__ = "follows"
- follower_id = db.Column(db.Integer, db.ForeignKey("users.id"), primary_key=True)
- followed_id = db.Column(db.Integer, db.ForeignKey("users.id"), primary_key=True)
- timestamp = db.Column(db.DateTime, default=datetime.utcnow)
- # SQLAlchemy 不能直接使用这个关联表, 因为如果这个做程序就无法访问其中的自定义字段. 相反的, 要把这个多对多关系的左右两侧拆分成两个基本的一对多关系, 而且要定义成标准的关系.
使用两个一对多关系实现的多对多关系:
1 |
class User(UserMixin, db.Model): |
3) 一对一
可以看做特殊的 一对多 关系. 但调用 db.relationship() 时 要把 uselist 设置 False, 把 多变为 一 .
4) 多对一
将 一对多 关系,反过来即可, 也是 一对多关系.
六. 数据库操作
1) 创建数据库及数据表
创建数据库
db.create_all()
示例 :
$ python myflask.py shell
> from myflask import db
> db.create_all()
如果使用 sqlite , 会在 SQLALCHEMY_DATABASE_URI 指定的目录下 多一个文件, 文件名为该配置中的文件名.
如果数据库表已经存在于数据库中, 那么 db.create_all() 不会创建或更新这个表.
更新数据库
方法一 :
先删除, 在创建 –> 原有数据库中的数据, 都会消失.
- > db.drop_all()
- > db.create_all()
方法二 :
数据库迁移框架 : 可以跟自动数据库模式的变化, 然后增量式的把变化应用到数据库中.
SQLAlchemy 的主力开发人员编写了一个 迁移框架 Alembic, 除了直接使用 Alembic wait, Flask 程序还可使用 Flask-Migrate 扩展, 该扩展对 Alembic 做了轻量级包装, 并集成到 Flask-Script 中, 所有操作都通过 Flaks-Script 命令完成.
① 安装 Flask-Migrate
$ pip install flask-migrate
② 配置
- from flask_migrate import Migrate, MigrateCommand
- # ...
- migrate = Migrate(app, db)
- manager.add_command('db', MigrateCommand)
③ 数据库迁移
a. 使用 init 自命令创建迁移仓库.
$ python myflask.py db init # 该命令会创建 migrations 文件夹, 所有迁移脚本都存在其中.
- b. 创建数据路迁移脚本.
- $ python myflask.py db revision # 手动创建 Alemic 迁移
- 创建的迁移只是一个骨架, upgrade() 和 downgrade() 函数都是空的. 开发者需要使用 Alembic 提供的 Operations 对象指令实现具体操作.
- $ python myflask.py db migrate -m COMMONT # 自动创建迁移.
- 自动创建的迁移会根据模型定义和数据库当前的状态之间的差异生成 upgrade() 和 downgrade() 函数的内容.
- ** 自动创建的迁移不一定总是正确的, 有可能漏掉一些细节, 自动生成迁移脚本后一定要进行检查.
- c. 更新数据库
- $ python myflask.py db upgrade # 将迁移应用到数据库中.
2) 插入行
模型的构造函数, 接收的参数是使用关键字参数指定的模型属性初始值. 注意, role 属性也可使用, 虽然他不是真正的数据库列, 但却是一对多关系的高级表示. 这些新建对象的 id 属性并没有明确设定, 因为主键是由 Flask-SQLAlchemy 管理的. 现在这些对象只存在于 Python 解释器中, 尚未写入数据库.
1 |
>> from myflask import db, User, Role >> db.create_all() >> 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=mod_role) >> user_david = User(username="david", role=user_role) >> admin_role.name |
3) 修改行
- >> admin_role = "Administrator"
- >> db.session.add(admin_role)
- >> db.session.commit()
4) 删除行
- >> db.session.delete(mod_role)
- >> db.session.commit()
5) 查询行
Flask-SQLAlchemy 为每个模型类都提供了 query 对象.
获取表中的所有记录
- >> Role.query.all()
- [<Role u'Admin'>, <Role u'Moderator'>, <Role u'User'>]
- >> User.query.all()
- [<Role u'john'>, <Role u'susan'>, <Role u'david'>]
查询过滤器
filter_by() 等过滤器在 query 对象上调用, 返回一个更精确的 query 对象. 多个过滤器可以一起调用, 直到获取到所需的结果.
- >> User.query.filter_by(role=user_role).all() # 以列表形式,返回所有结果,
- >> User.query.filter_by(role=user_role).first() # 返回结果中的第一个.
filter() 对查询结果过滤,比”filter_by()”方法更强大,参数是布尔表达式
- # WHERE age<20
- users = User.query.filter(User.age<20)
- # WHERE name LIKE 'J%' AND age<20
- users = User.query.filter(User.name.startswith('J'), User.age<20)
查询过滤器 :
过滤器 | 说明 |
---|---|
filter() | 把过滤器添加到原查询上, 返回一个新查询 |
filter_by() | 把等值过滤器添加到原查询上, 返回一个新查询 |
limit() | 使用是zing的值限制原查询返回的结果数量, 返回一个新查询 |
offset() | 偏移原查询返回的结果, 返回一个新查询 |
order_by() | 根据指定条件对原查询结果进行排序, 返回一个新查询 |
group_by() | 根据指定条件对原查询结果进行分组, 返回一个新查询 |
查询执行函数 :
方法 | 说明 |
---|---|
all() | 以列表形式返回查询的所有结果 |
first() | 返回查询的第一个结果,如果没有结果,则返回 None |
first_or_404() | 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应 | |
| get() | 返回指定主键对应的行,如果没有对应的行,则返回 None |
get_or_404() | 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 | |错误响应
| count() | 返回查询结果的数量 |
| paginate() | 返回一个 Paginate 对象,它包含指定范围内的结果 |
6) 会话管理, 事务管理
单个提交
- >> db.session.add(ONE)
- >> db.session.commit()
多个提交
- >> db.session.add_all([LIST_OF_MEMBER])
- >> db.session.commit()
删除会话
- >> db.session.delete(mod_role)
- >> db.session.commit()
事务回滚 : 添加到数据库会话中的所有对象都会还原到他们在数据库时的状态.
>> db.session.rollback()
七. 视图函数中操作数据库
1 |
@app.route('/', methods=['GET', 'POST']) |
八. 分页对象 Pagination
1. paginate() 方法
paginate() 方法的返回值是一个 Pagination 类对象, 该类在 Flask-SQLAlchemy 中定义, 用于在模板中生成分页链接.
- paginate(页数[,per_page=20, error_out=True])
- 页数 : 唯一必须指定的参数,
- per_page : 指定每页现实的记录数量, 默认 20.
- error_out : True 如果请求的页数超出了返回, 返回 404 错误; False 页数超出范围时返回一个,空列表.
示例代码:
1 |
@main.route("/", methods=["GET", "POST"]) |
2. 分页对象的属性及方法:
Flask_SQLAlchemy 分页对象的属性:
属性 | 说明 |
---|---|
items | 当前分页中的记录 |
query | 分页的源查询 |
page | 当前页数 |
prev_num | 上一页的页数 |
next_num | 下一页的页数 |
has_next | 如果有下一页, 返回 True |
has_prev | 如果有上一页, 返回 True |
pages | 查询得到的总页数 |
per_page | 每页显示的记录数量 |
total | 查询返回的记录总数 |
在分页对象可调用的方法:
方法 | 说明 |
---|---|
iter_pages(left_edge=2,left_current=2,right_current=5,right_edge=2) | 一个迭代器, 返回一个在分页导航中显示的页数列表. 这个列表的最左边显示 left_edge 页, 当前页的左边显式 left_current 页, 当前页的右边显示 right_currnt 页, 最右边显示 right_edge 页. 如 在一个 100 页的列表中, 当前页为 50 页, 使用默认配置, 该方法返回以下页数 : 1, 2, None, 48,49,50,51,52,53,54,55, None, 99 ,100. None 表示页数之间的间隔. |
prev() | 上一页的分页对象 |
next() | 下一页的分页对象 |
3. 在模板中与 BootStrap 结合使用示例
使用 Flaks-SQLAlchemy 的分页对象与 Bootstrap 中的分页 CSS, 可以轻松的构造出一个 分页导航.
分页模板宏 _macros.html : 创建一个 Bootstrap 分页元素, 即一个有特殊样式的无序列表.
1 |
{% macro pagination_widget(pagination,endpoint) %} |
导入使用分页导航
1 |
{% extends "base.html" %} |
九. 监听事件
1. set 事件
示例代码 :
1 |
from markdown import markdown |
十. 记录慢查询.
十一. Binds 操作多个数据库
十二. 其他
1. ORM 在查询时做初始化操作
当 SQLIAlchemy ORM 从数据库查询数据时, 默认不调用__init__
方法, 其底层实现了 Python 类的 __new__()
方法, 直接实现 对象实例化, 而不是通过 __init__
来实例化对象.
如果需要在查询时, 依旧希望实现一些初始化操作, 可以使用 orm.reconstructor()
装饰器或 实现 InstanceEvents.load()
监听事件.
1 |
# orm.reconstructor |
如果只是希望在从数据库查询生成的对象中包含某些属性, 也可以使用 property
实现:
1 |
class AwsRegions(db.Model): |
转自:https://www.pyfdtic.com/2018/03/19/flaskExt--flask-sqlalchemy/
flask-sqlalchemy用法详解的更多相关文章
- C#中string.format用法详解
C#中string.format用法详解 本文实例总结了C#中string.format用法.分享给大家供大家参考.具体分析如下: String.Format 方法的几种定义: String.Form ...
- @RequestMapping 用法详解之地址映射
@RequestMapping 用法详解之地址映射 引言: 前段时间项目中用到了RESTful模式来开发程序,但是当用POST.PUT模式提交数据时,发现服务器端接受不到提交的数据(服务器端参数绑定没 ...
- linux管道命令grep命令参数及用法详解---附使用案例|grep
功能说明:查找文件里符合条件的字符串. 语 法:grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数>] ...
- mysql中event的用法详解
一.基本概念mysql5.1版本开始引进event概念.event既“时间触发器”,与triggers的事件触发不同,event类似与linux crontab计划任务,用于时间触发.通过单独或调用存 ...
- CSS中伪类及伪元素用法详解
CSS中伪类及伪元素用法详解 伪类的分类及作用: 注:该表引自W3School教程 伪元素的分类及作用: 接下来让博主通过一些生动的实例(之前的作业或小作品)来说明几种常用伪类的用法和效果,其他的 ...
- c++中vector的用法详解
c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间 ...
- AngularJS select中ngOptions用法详解
AngularJS select中ngOptions用法详解 一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...
- systemctl命令用法详解
systemctl命令用法详解系统环境:Fedora 16binpath:/bin/systemctlpackage:systemd-units systemctl enable httpd.serv ...
- CSS3的@keyframes用法详解:
CSS3的@keyframes用法详解:此属性与animation属性是密切相关的,关于animation属性可以参阅CSS3的animation属性用法详解一章节. 一.基本知识:keyframes ...
- window.onload用法详解:
网页中的javaScript脚本代码往往需要在文档加载完成后才能够去执行,否则可能导致无法获取对象的情况,为了避免这种情况的发生,可以使用以下两种方式: 一.将脚本代码放在网页的底端,这样在运行脚本代 ...
随机推荐
- Oracle系列六 分组函数
分组函数作用于一组数据,并对一组数据返回一个值. 组函数类型 AVG COUNT MAX MIN STDDEV SUM 组函数语法 SELECT [column,] group_function(co ...
- [报错处理]Python Requests - No connection adapters
出错信息很清楚:Python请求 - 没有连接适配器. 你得把网络协议加进入网址: http://192.168.1.61:8080/api/call 没有 http:// 请求不知道如何连接远程. ...
- xshell修改配色方案为白色
- [转]git登录账号密码错误remote: Incorrect username or password
链接地址:https://baijiahao.baidu.com/s?id=1622020216177100162&wfr=spider&for=pc
- [LeetCode] 140. Word Break II 单词拆分II
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add space ...
- [LeetCode] 216. Combination Sum III 组合之和 III
Find all possible combinations of k numbers that add up to a number n, given that only numbers from ...
- [LeetCode] 250. Count Univalue Subtrees 计算唯一值子树的个数
Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of ...
- springboot-把web项目打成war包部署到外部tomcat
将打包方式修改为war <packaging>war</packaging> 移除tomcat依赖或者将tomcat依赖scope改为provide 移除tomcat依赖 &l ...
- 【视频开发】 十全大补:CxImage图像处理类库
十全大补:CxImage图像处理类库 转载IT168 CxImage是一个可以用于MFC 的C++图像处理类库类,它可以打开,保存,显示,转换各种常见格式的图像文件,比如BMP, JP ...
- Android中的数据结构
数据结构在Android中也有着大量的运用,这里采用数据结构与源代码分析相结合,来认识Android的数据结构 线性表 线性表可分为顺序存储结构和链式存储结构 顺序存储结构-ArrayList 通过对 ...