天天看点

Flask:使用SQLAlchemy持久化数据

首先,安装flask-sqlalchemy扩展:

$pip install flask-sqlalchemy

然后,在项目中导入SQLAlchemy类,并实例化应用程序使用的数据库(以mysql为例):

db对象是SQLAlchemy类的实例,表示程序使用的数据库,同时还获得了Flask-SQLAlchemy提供的所有功能。

在Python中,可以使用数据库相应的包直接操作数据库,如PyMySQL操作MySQL数据库,还有一些数据库抽象层代码包供选择,如这里要讨论的SQLAlchemy。该抽象包直接处理高等级的Python对象,而不用处理如表这样的数据库实体。

抽象层,就是所谓的对象关系映射(ORM),其最大的优势就是:能在用户不知觉的情况下把高层的面向对象操作转换成低层的数据库指令,极大简化代码编写。SQLAlchemy就是已与Flask很好集成的更高层抽象例子,其还支持多种关系型数据库引擎。

基于SQLAlchemy的模型定义:

tablename定义在数据库中使用的表名;

db.Column类构造函数的第1个参数是数据库表列(也是模型属性)的类型,其余的参数指定属性(数据库表列)的配置选项。

2.1 常用的SQLAlchemy列类型

类型名

Python类型

说明

Integer

int

普通整数,一般32位

SmallInteger

取值范围小的整数,一般16位

BigInteger

int或long

不限制精度的整数

Float

float

浮点数

String

str

变长字符串

Boolean

bool

布尔值

Date

datetime.date

日期

Time

datetime.time

时间

DateTime

datetime.datetime

日期和时间

Text

变长字符串,对较长或不限长度的字符串做了优化

Numeric

decimal.Decimal

定点小数

2.2 常用的SQLAlchemy列选项

选项名

primary_key

如果设为True,这列就是表的主键

unique

如果设为True,这列不允许重复值

index

如果设为True,为该列创建索引,提升查询效率

nullable

如果设为True,这列允许null值,如果设为false,不允许为空

default

为这列定义默认值

关系型数据库使用关系把不同表中的行联系起来。以上述模型定义代码为例,假设角色对用户是一对多的关系(即1个角色可属于多个用户,而每个用户只能有1个角色)。

在“多”的一方,使用外键定义关系

如在Users模型中,定义role_id列为外键,给db.Column构造函数传递db.ForeignKey()参数,并将roles.id作为db.ForeignKey()的参数,其表明role_id列的值是roles表中行的id值。

在“一”的一方,基于面向对象的视角创建代表实例(记录或行)的属性,如上述的users = db.relationship('Users', backref='role')

db.relationship()的第1个参数表明这个关系的另一端是哪个模型,backref参数向Users模型添加一个role属性,从而定义反向关系。通过这一属性(role)可以替代role_id访问Roles模型,此时将获取的是模型对象,而不是外键的值。

如何理解上述这段话,可以从下面2句代码加深:

通过第1句代码,可以直接获得特定角色实例(inst_role)相对应的所有users对象,且是以列表形式返回。

通过第2句代码,可以通过user实例直接获得该user所对应的role对象(1行记录,而不是Users模式定义的role_id字段值)。

要定义一对一的关系,只需基于一对多的模型定义基础上,给db.relationship()函数多传一个关键字表示关系选项:

users = db.relationship('Users', backref='role', userlist=False)

一对多与一对一在编码时,有个点需特别注意:当通过“一”的实例(db.relationship定义方)获取多的一方的对象时:

一对多:users = inst_role.users返回的是对象列表

一对一:users = inst_role.users返回的就是对象,而非列表

Pending...

在Flask-SQLAlchemy中,对数据库所做的改动均是通过数据库“会话”进行管理的,会话用db.session表示。如需用db.session.commit()提交对记录的修改,始终把数据库相关改动放在会话中提交,可避免因部分更新异常导致数据库中数据的不一致性。

前4行代码,实例化2种角色及2个用户对象(映射至数据库即是给记录的字段赋值)

5~7行代码就是将新增角色及用户操作放在了1个session中,最后再统一提交(commit),可防止因其中某条语句异常而更新部分从而导致数据的不一致性。该操作就是将多个原子操作组成一个事务,如果某条更新失败就会导致整个会话失效。

5~7行还可简写成: db.session.add([admin_role, mod_role, john, david])

在Flask-SQLAlchemy中,一条记录表示为一个对象;记录的字段表示为对象的属性,因此要更新字段值,实际上就是对对象的属性赋值:

查询表中所有记录:模式.query.all()

eg. Roles.query.all()

使用过滤器进行更精确查询

过滤器

filter

把过滤器添加到原查询上

filter_by

把等值过滤器添加到原查询上

limit

使用指定的值限制原查询返回的结果数量

offset

偏移原查询返回的结果

order_by

根据指定条件对原查询结果进行排序

group_by

根据指定条件对原查询结果进行分组

2.1 filter与filter_by区别

2.1.1 语法区别

filter需要用“类名.属性名”且需用==比较,而filter_by直接用属性名,比较用=

2.1.2 组合查询

filter不支持组合查询,只能连续用filter来实现,而filter_by支持组合查询(下面2条语句效果一样)

注:如果要查看SQLAlchemy为查询生成的原生SQL查询语句,只需把query对象转换成字符串: str(Users.query.filter_by(role=admin_role))

执行查询

在查询上应用指定的过滤器后,通过调用all()触发执行查询,常见的触发执行方法有:

方法

all()

查询所有结果

first()

返回查询的第1个结果,没有返回None

get()

返回指定主键对应的行,没有返回None

count()

返回查询结果的数量

first_or_404()

返回查询的第1个结果,如果没有结果,则终止请求,返回404错误响应

get_or_404()

返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回404错误响应

paginate()

返回一个paginate对象

基于关系的对象获取

4.1 一对多

4.1.1 从“一”获取对应的所有多端对象

注:inst_role.users获取对象时,隐含了调用all()方法触发执行,但如果像加一些过滤器(如排序),则需要在db.relationship中添加lazy='dynamic'关键字参数。然后即可引入过滤器:inst_role.users.order_by(Users.username)

4.2 多对多

在Flask-SQLAlchemy中,删除数据库记录,可映射至删除代表该记录的对象:

参考资料

继续阅读