上篇我们使用原生的SQL进行更新数据库,这篇我们学习下使用ORM。
我们看下使用ORM的一些好处:
- 隔离数据库之间的差异(不在乎数据库驱动和数据类型,接口一致)
- 便于维护
orm会提供防止sql注入等功能- 变量传递式的调用更加简单
这节我们学习的ORM框架是peewee。peewee简单,灵活,申明方式和django的orm接近。
其中async-peewee是基于asyncio和peewee的异步IO库。
model的定义和表的自动生成
这个小节我们将会学习如何想使用Django ORM一样由类创建成表。
如何创建一个model:
1 | from peewee import * |
CharField需要指定最大长度max_length.使用ForeignKeyField建立外键关系。里面很多字段含义和django orm类似。
上面完成之后我们就可以直接创建数据表了
1 | def init_table(): |
我们看下生成的数据表

虽然我们没有自己定义id为主键,peewee为我们自动添加了一个值为id的主键。而SQLAlchemy则是必须指定主键的。
我们可以创建一个基类将指定数据库连接对象放到基类中,这样其他需要指定数据库连接的只需要继承基类即可。
1 | class BaseModel(Model): |
下面我们创建具有关联关系的两个模型。
1 | class Supplier(BaseModel): |
创建数据表
1 | def init_table(): |

我们看到在数据表里保存的是supplier_id而不是supplier。我们在查询的时候如果使用supplier传入的是一个实例对象,使用supplier_id则是一个数值。这个和django的一致。
model的数据保存
在上面我们创建数据表的时候,使用了IntegerField等字段类型来指定数据类型,那peewee都有哪些字段数据类型呢,每个字段类型又有哪些属性呢?
我们可以通过查看官方文档得知:Models and Fields
通过类的实例化形式来保存数据
1 | def save_model(): |
直接使用save方法即可保存到数据库。

我们看到数据库自己为我们新增了一个主键id值。
我们在调用
save方法之前 实例supplier的id值是为空的,只有调用save之后才有值。这一点和其他ORM框架类似。
如果数据是字典类型,我们可以直接使用解包功能创建类实例。
1 | goods_list = [ |
注意字典的键要和模型的字段值相同。
peewee查询数据
查询的官方文档:Querying
查询获取一条数据
1 | # 通过 id 获取数据 |
如果数据不存在,将会抛出异常
获得所有数据
1 | goods = Goods.select() |
上面
get_by_id和select的返回值是不一样的,前者返回的数据库实例对象,是直接拼凑好SQL语句去数据库查询得到的结果。后者返回的是ModelSelect对象,不是真正数据库执行的结果。只有我们执行了下面的迭代查询之后才真正去数据库执行SQL。(底层原理是python的迭代协议)
这点和Django的查询类似。
获取指定条件的数据
1 | goods = Goods.select(Goods.name, Goods.price) |
如果指定了某些字段,在遍历的时候获取没有指定字段数据的时候将返回None。
根据条件查询
1 | # select * from goods where price > 100 |
很像原生的SQL
and条件查询
1 | #select * from goods where price>100 and click_num>200 |
or条件查询
1 | #select * from goods where price>100 or click_num>200 |
like条件查询
1 | #select * from goods where name like "%飞天" |
这个很像Django的
in条件查询
1 | goods = Goods.select().where(Goods.id<<[1,3]) |
使用<<代表in_操作。
关于操作符的文档:Query operators
表内字段比较查询
1 | # select * from goods where price > click_num |
排序查询
1 | #排序 select * from goods order by price desc |
分页查询
1 | #分页 |
官方示例文档:Query Examples
数据更新和删除
更新数据
获得对应数据,使用赋值方式更新
1 | good = Goods.get_by_id(1) |
使用更新语句
1 | # update click_num=100 where id =1 |
这里需要我们再次调用execute去真正执行SQL,这个execute是同步操作,正是查询和拼接SQL是分离的,将peewee变成异步才有可能。
使用
update更新的字段不需要指定模型
删除数据
获得想删除的数据对象,使用delete_instance删除。
1 | good = Goods.get_by_id(1) |
使用delete语句删除数据
1 | #delete from goods where price>150 |
delete语句同样不是直接去数据库执行
通过peewee-async集成到tornado
上面学习的peewee是同步的ORM框架,如果我们想在tornado中使用,我们需要异步的ORM。
peewee-async是将peewee变成异步的一个库
我们直接看下官方的示例:
1 | import asyncio |
我们之前peewee使用很多同步方法都被peewee_async做成了协程方式,可以看下源码结构知道哪些是协程方式了。

几篇不错的参考文章:
使用WTForms进行数据验证
这个小节和之前在学习Flask中使用WTForms中试一致的。
注意一点:
如果我们在tornado中直接使用wtforms的话是会报错的。
1 | message_from = MessageForm(self.request.arguments) |
我们可以安转一个wtforms-tornado库来解决
安装成功后将继承的基类Form换成wtforms_tornado的。
1 | from wtforms_tornado import Form |
这样直接使用self.request.arguments就不会报错了。
我们看下模板中如何使用form
1 | {% autoescape None %} |