当前位置: 首页 > 工具软件 > mysql-async > 使用案例 >

tornado + peewee-async 异步ORM

楮景明
2023-12-01

前言

习惯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 搭建整体框架。

tornado + peewee-async

修正文档中给出的示例,能够正常运行:

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给出的示例,应有尽有。

tips

yield from

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嵌套时的遍历。

async和 await

在上面的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调用。

暂且至此。

 类似资料: