上篇我们使用原生的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 %} |