习惯sql操作数据,需要做一些简单业务操作时sql操作数据的低复用显露无疑。惰性不愿手动写ORM,索性找一个python比较通用的ORM,选了peewee。为和tornado配合使用,改用在peewee基础上封装的peewee-async几经折腾,总算调试通过,演示环境MacOS,python3.5.2。
类似还有torpeewee,不推荐使用。
本篇所有演示代码可以在个人github中下载,演示demo。
peewee的使用文档
peewee-async的使用文档
直接pip就能安装以上package
pip install peewee peewee-async
# 如果需要操作MySQL数据库需要额外安装
pip install aiomysql
# 如果需要操作PostgreSQL数据库需要额外安装
pip install aiopg
如果有使用Django的经验,基本秒懂peewee ORM的使用方式,整体上就是和Django类似的定义和使用。
在peeweee-aysnc的使用文档中给出了peeweee-aysnc + tornado的使用范例,根本跑不通,以下使用tornado 5.0配合 peewee-async 0.5.11,peewee 2.9.1,iomysql 0.0.12 搭建整体框架。
修正文档中给出的示例,能够正常运行:
import peewee
import peewee_async
import tornado.web
from tornado import httpserver
database = peewee_async.PooledMySQLDatabase(
'alexdb', host='127.0.0.1', port=3306,
user='root', password='')
# Define model
class TestNameModel(peewee.Model):
name = peewee.CharField()
class Meta:
database = database
def __str__(self):
return self.name
# Create table, add some instances
TestNameModel.create_table(True)
TestNameModel.get_or_create(id=1, defaults={'name': "TestNameModel id=1"})
TestNameModel.get_or_create(id=2, defaults={'name': "TestNameModel id=2"})
TestNameModel.get_or_create(id=3, defaults={'name': "TestNameModel id=3"})
database.close()
# Define handlers
class RootHandler(tornado.web.RequestHandler):
"""
Accepts GET methods.
GET: get instance by id, `id` argument is required
"""
async def get(self):
obj_id = self.get_argument('id', None)
try:
obj = await self.application.objects.get(TestNameModel, id=obj_id)
self.write({
'id': obj.id,
'name': obj.name,
})
except TestNameModel.DoesNotExist:
raise tornado.web.HTTPError(404, "Object not found!")
def main():
app = tornado.web.Application(handlers=[
(r'/alex', 'demo.RootHandler'), ], debug=True)
# Set up database and manager
app.objects = peewee_async.Manager(database)
# Run loop
print("""Run application server http://127.0.0.1:8888s
Try GET urls:
http://127.0.0.1:8888?id=1
^C to stop server""")
server = httpserver.HTTPServer(app, xheaders=True)
server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
页面访问 http://127.0.0.1:8888/alex?id=1能够正常查询数据。更多的ORM查询语法可以参见peewee的查询方式。
注意:
1,上述代码在tornado 4.4.2版本中不能正常运行,5.0中可以。
2,peewee-async在get数据时,如果没有检索到符合条件的数据会触发一个DoesNotExist的异常。
3,跟多的peewee ORM的细节使用方式请查询peewee给出的示例,应有尽有。
python之yield(一) 和 python之yield(二) 给出了较为详细阐述,补充说明下yield from,python官网给出的解释。
如果真的看解释,比较繁琐,看一个示例,有一个列表其中的元素可能是嵌套的列表,我想要遍历其中所有元素。
def flatten(source):
for sub in source:
if isinstance(sub, list):
yield from flatten(sub)
else:
yield sub
source_list = [1, 'alex', ['hello', 'world', ['inner', 'data']], 2, [3, 4]]
for obj in flatten(source_list):
print(obj)
# 运行结果:1 alex hello world inner data 2 3 4 依次打印出
包含yield的函数返回的不是结果是一个生成器,需要next去触发返回结果。但是如果yeild后得到仍然是一个generator并非真正的值,可以用 yield from用以将sub_generator 的控制权交给主调,去遍历sub_generator 。实质上就是一个generator嵌套时的遍历。
在上面的tornado代码中用的并非 tornado的 gen.coroutine来装饰方法,内部用yield获取结果的形式,改用了async和await。tornado的异步请参见之前的 tornado异步机制浅析。
首先async和await是python 3.5引入的关键字。PEP 492 给出了详细介绍。
狭窄一点理解就是在此前的python中为能够完成异步方法的定义,使编程更加pthonic。
import time
import random
import asyncio
@asyncio.coroutine
def yield_fib(n):
start = time.time()
a, b, index, sum_sleep = 0, 1, 0, 0
while index < n:
sleep_secs = random.uniform(0, 0.5)
sum_sleep += sleep_secs
yield from asyncio.sleep(sleep_secs)
print('yield_fib think {} secs to get {}'.format(sleep_secs, b))
a, b = b, a + b
index += 1
print('using: {} sec, sleep: {} sec'.format(time.time() - start, sum_sleep))
async def async_fib(n):
start = time.time()
a, b, index, sum_sleep = 0, 1, 0, 0
while index < n:
sleep_secs = random.uniform(0, 0.2)
sum_sleep += sleep_secs
await asyncio.sleep(sleep_secs)
print('async_fib think {} secs to get {}'.format(sleep_secs, b))
a, b = b, a + b
index += 1
print('using: {} sec, sleep: {} sec'.format(time.time() - start, sum_sleep))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(yield_fib(10)),
asyncio.ensure_future(async_fib(10))
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
以上在 python asyncio包的使用过程中,可以用async / await的关键字取代原先的 @asyncio.coroutine / yield from。但是在之前给出tornado中 async/awai 用以取代 tornado.gen.coroutine / yield这是由于 二者对coroutine 定义不一样,核心思想区别无几。
注意:
tornado框架从 4.3版本以上支持 用 async取代 tornado.gen.coroutine装饰异步方法和 await取代yeild调用。
暂且至此。