I、Flask数据关联模型SQLAlchemy
Flask-SQLAlchemy使用起来非常有趣,对于基本应用十分容易使用,并且对于大型项目易于扩展。有关完整的指南,请参见SQLAlchemy
API文档。
常见情况下对于只有一个 Flask 应用,所有您需要做的事情就是创建 Flask 应用,选择加载配置接着创建 SQLAlchemy
对象时候把 Flask 应用传递给它作为参数。
一旦创建,这个对象就包含 sqlalchemy
和 sqlalchemy.orm
中的所有函数和助手。此外它还提供一个名为 Model
的类,用于作为声明模型时的 delarative 基类
普通的 SQLAlchemy 不同之处:
1.SQLAlchemy
允许您访问下面的东西 :
·sqlalchemy
和sqlalchemy.orm
下所有的函数和类
·一个叫做session
的预配置范围的会话(session)
·metadata
属性
·engine
属性
·SQLAlchemy.create_all()
和SQLAlchemy.drop_all()
,根据模型用来创建以及删除表格的方法
· 一个Model
基类,即是一个已配置的声明(declarative)的基础(base)
2.Model
声明基类行为类似一个常规的 Python 类,不过有个query
属性,可以用来查询模型 (Model
和BaseQuery
)
3. 您必须提交会话,但是没有必要在每个请求后删除它(session),Flask-SQLAlchemy 会帮您完成删除操作。
什么是ORM?
ORM是用来把对象模型表示的对象映射到基于SQL的关系模型数据库结构中去。简明点说,数据库中每一行就是一个对象。
II、相关的配置字段
在上一章内,我们在db_demo内应用课许多诸如app.config['SQLALCHEMY_DATABASE_URI'] = xxx
的语句,这些语句为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_NATIVE_UNICODE | 可以用于显式禁用原生 unicode 支持。当使用不合适的指定无编码的数据库默认值时,这对于 一些数据库适配器是必须的(比如 Ubuntu 上某些版本的 PostgreSQL )。 |
SQLALCHEMY_POOL_SIZE | 数据库连接池的大小。默认是引擎默认值(通常 是 5 ) |
SQLALCHEMY_POOL_TIMEOUT | 设定连接池的连接超时时间。默认是 10 。 |
SQLALCHEMY_POOL_RECYCLE | 多少秒后自动回收连接。这对 MySQL 是必要的, 它默认移除闲置多于 8 小时的连接。注意如果 使用了 MySQL , Flask-SQLALchemy 自动设定这个值为 2 小时。 |
III、常见数据模型字段
类型名 | python中类型 | 说明 |
---|---|---|
Integer | int | 普通整数,一般是32位 |
SmallInteger | int | 取值范围小的整数,一般是16位 |
BigInteger | int/long | 不限制精度的整数 |
Float | float | 浮点数 |
Numeric | decimal.Decimal | 普通整数,一般是32位 |
String | str | 变长字符串 |
Text | str | 变长字符串,对较长或不限长度的字符串做了优化 |
Unicode | unicode | 变长Unicode字符串 |
UnicodeText | unicode | 变长Unicode字符串,对较长或不限长度的字符串做了优化 |
Boolean | bool | 布尔值 |
Date | datetime.date | 时间 |
Time | datetime.datetime | 日期和时间 |
LargeBinary | str | 二进制文件 |
IV、约束字段与表间关系
由于Flask-SQLAlchemy不自动设置表内的字段的约束与,因此需要在设计数据字段时自行添加
那么常见的约束字段有
选项名 | 说明 |
---|---|
primary_key | 如果为True,代表表的主键 |
unique | 如果为True,代表这列不允许出现重复的值 |
index | 如果为True,为这列创建索引,提高查询效率 |
nullable | 如果为True,允许有空值,如果为False,不允许有空值 |
default | 为这列定义默认值 |
autoincrement | 自增 |
由于不自行设计id值(与Django的一定区别)Flask-SQLAlchemy并不涉及到表间关系的自动配置,因此需要了解表间关系联系字段
常见的表间关系字段有
选项名 | 说明 |
---|---|
backref | 在关系的另一模型中添加反向引用,用于设置外键名称,在1内查n的 |
primary join | 明确指定两个模型之间使用的联结条件 |
uselist | 如果为False,不使用列表,而使用标量值 |
order_by | 指定关系中记录的排序方式 |
secondary | 指定多对多关系中关系表的名字 |
secondary join | 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件 |
lazy | 指定如何加载相关记录。 |
lazy的可选值有
属性 | 说明 |
---|---|
select | 首次访问时按需加载 |
immediate | 源对象加载后就加载 |
joined | 加载记录,但使用联结 |
subquery | 立即加载,但使用子查询 |
noload | 永不加载 |
dynamic | 不加载记录,但提供加载记录的查询 |
V、查询执行器
执行器 | 说明 |
---|---|
all() | 以列表形式返回查询的所有结果 |
first() | 返回查询的第一个结果,如果没有结果,则返回 None |
first_or_404() | 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应 |
get() | 返回指定主键对应的行,如果没有对应的行,则返回 None |
get_or_404() | 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错误响应 |
count() | 返回查询结果的数量 |
paginate() | 返回一个 Paginate 对象,它包含指定范围内的结果 |
VI、过滤器
过滤器 | 说明 |
---|---|
filter() | 把过滤器添加到原查询上,返回一个新查询 |
filter_by() | 把等值过滤器添加到原查询上,返回一个新查询 |
limit | 使用指定的值限定原查询返回的结果 |
offset() | 偏移原查询返回的结果,返回一个新查询 |
order_by() | 根据指定条件对原查询结果进行排序,返回一个新查询 |
group_by() | 根据指定条件对原查询结果进行分组,返回一个新查询 |
注
filter_by用于查询简单的列名,不支持比较运算符。
比filter_by的功能更强大,支持比较运算符,支持or_、in_等语法。
VII、一个简单的建表实例
我们基于上一章中的db_demo.py的内容展开,在每一个修改的实例内增加一个__repr__
方法,简单介绍一下__repr__
:当我们需要对一个函数中传入某个参数后的状态需要打包打印,或者显示在屏幕上时我们可以在这个函数内部定义一个__repr__
函数,可以通过这个函数,对你想要打印的内容进行设置。
修改db_demo.py
class Role(db.Model):
# 定义表名
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
db.relationship('User', backref='role')
def __repr__(self):
return 'Role:{}'.format(self.name)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
email = db.Column(db.String(64), unique=True)
password = db.Column(db.String(64), unique=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return 'User:{}'.format(self.name)
VIII、一些查询的联系
在此前,我们修改db_demo内的main,以用来插入一些数据,当然,这种方式并非日后的常用方法,这里只作为一种简单的填充
if __name__ == '__main__':
# 删除表
db.drop_all()
# 创建表
db.create_all()
ro1 = Role(name='admin')
ro2 = Role(name='user')
db.session.add_all([ro1, ro2])
db.session.commit()
us1 = User(name='wang', email='wang@163.com', password='123456', role_id=ro1.id)
us2 = User(name='zhang', email='zhang@189.com', password='201512', role_id=ro2.id)
us3 = User(name='chen', email='chen@126.com', password='987654', role_id=ro2.id)
us4 = User(name='zhou', email='zhou@163.com', password='456789', role_id=ro1.id)
us5 = User(name='tang', email='tang@neuedu.com', password='158104', role_id=ro2.id)
us6 = User(name='wu', email='wu@gmail.com', password='5623514', role_id=ro2.id)
us7 = User(name='qian', email='qian@gmail.com', password='1543567', role_id=ro1.id)
us8 = User(name='liu', email='liu@neuedu.com', password='867322', role_id=ro1.id)
us9 = User(name='li', email='li@163.com', password='4526342', role_id=ro2.id)
us10 = User(name='sun', email='sun@163.com', password='235523', role_id=ro2.id)
db.session.add_all([us1, us2, us3, us4, us5, us6, us7, us8, us9, us10])
db.session.commit()
db.session.add_all([us1, us2])
db.session.commit()
app.run(port=5005,debug=True)
1、返回名字等于wang 的所有人
User.query.filter_by(name='wang').all()
2、返回查询的第一个对象
User.query.first()
3、返回所有查询对象
User.query.all()
4、返回名字结尾字符为g的所有数据
User.query.filter(User.name.endswith("g")).all()
User.query.filter(User.name.startswith("w")).all()
User.query.filter(User.name.contains("n")).all()
User.query.filter(User.name.like("%n%g")).all()
5、返回user中id = 1的数据
User.query.get(1)
6、返回名字不等于wang的所有数据
User.query.filter(not_(User.name == "wang")).all()
User.query.filter(User.name != "wang").all()
7、返回名字不等于 wang 且 邮箱 以 163.com结尾的所有
User.query.filter(or_(User.name != 'wang',User.email.endswith('163.com') )).all()
8、查询名字和邮箱都以 li 开头的所有数据
User.query.filter(User.name.startswith("li"), User.email.startswith("li")).all()
User.query.filter(and_(User.name.startswith("li"), User.email.startswith("li"))).all()
9、查询password是 123456
或者 email
以 neuedu.com
结尾的所有数据
User.query.filter(or_(User.password == '123456', User.email.endswith('neuedu.com'))).all()
10、查询id为 [1, 3, 5, 7, 9] 的用户列表
User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all()
11、 查询name为liu的角色数据
User.query.filter(User.name == 'liu').all()
12、查询所有用户数据,并以邮箱排序
User.query.order_by(User.email.desc()).all()
13、每页3个,查询第2页的数据
pn = User.query.paginate(2, 3)
pn.items # 获取该页内容
pn.page # 获取该页页码
pn.pages # 获取总页数
14、定义模型,并显示作者名和书名
作者Author(),表名 author,属性有:id,name,au_book(外键)
书名Book(),表名 books ,属性有:id,name,au_book
class Author(db.Model):
__tablename__ = 'author'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
au_book = db.Column(db.Integer, db.ForeignKey('book.id'))
def __repr__(self):
return 'Author:{}'.format(self.name)
class Book(db.Model):
__tablename__ = 'books'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
au_book = db.relationship('Book', backref='book')
def __repr__(self):
return 'Book:{}'.format(self.name)
IX、增删改的操作
Flask-SQLAlchemy的增删改操作可以理解为是通过与数据库建立一个小型会话的方式进行的,下面介绍一下这些基于db = SQLAlchemy(app)
的操作
操作 | 说明 |
---|---|
create_all() | 建表操作,db.create_all() |
drop_all() | 删除表操作,db.drop_all() |
add_all() | 插入操作,db.seesion.add_all([]),插入可选择使用list进行批量插入 |
commit() | 提交操作,db.session.commit(),提交其语句以上的所有更改数据库内容的操作 |
delete() | 删除操作,db.session.delete(),删除一条数据 |
更新操作只需要选择需要更新的对象,修改其属性值然后提交即可。