tornado mysql 异步_peewee与异步操作

在《peewee用法考察》中,我初步掌握peewee的一些常用操作。而做这些准备,是为了在tornado中使用。

tornado是一个异步网络IO非阻塞框架,这意味着涉及到IO阻塞操作,我们都应该以异步的形式去进行。而peewee本身并不是异步的,因此我们还需要引入另外一些库才能更好的契合tornado。

我们需要什么类库

peewee-async

aiomysql

peewee-async是一个基于asyncio的封装成的库,用于进行peewee异步操作。而peewee是基于pymysql进行操作的,pymysql异步操作mysql则需要aiomysql库的支持。

注意:目前支持tornado3.0+的peewee-async属于预发行版本,需要添加-pre参数下载。

Version 0.6.0a is published as pre-release, mind the "a" in version identifier. That means in order to install it you should specify --pre flag for pip.

pip install -pre peewee-async

pip install aiomysql

与tornado结合:同步阻塞下的情况

# -*- coding: utf-8 -*-

from tornado.web import Application, RequestHandler

from tornado.httpserver import HTTPServer

from tornado.ioloop import IOLoop

from models import *

import sys

class MainHandler(RequestHandler):

def get(self):

uid = self.get_argument('uid', 10000)

query = User.select(User.uname).where(User.uid == uid).get()

self.write('uname:%s' % query.uname)

class TestHandler(RequestHandler):

def get(self):

self.write('test here')

def main():

app = Application([

(r'/', MainHandler),

(r'/test', TestHandler),

])

try:

server = HTTPServer(app)

server.listen(8000)

IOLoop.current().start()

except KeyboardInterrupt:

print('Exit')

if __name__ == '__main__':

sys.exit(main())

启动mysql交互界面,锁住user表的读写操作,模拟硬盘IO阻塞。

use peewee;

lock table user write;

然后先后访问localhost:8000/和localhost:8000/test,我们会发现两个访问都阻塞在请求状态,直到我们进行

unlock table;

解锁写锁之后才能获得数据。

与tornado结合:异步硬盘IO非阻塞下的情况

为了使用peewee-async,首先我们需要对models.py文件一点修改:

# -*- coding: utf-8 -*-

from peewee import *

"""引入peewee_async相关类库"""

from peewee_async import MySQLDatabase as AsyncMySQLDatabase, Manager

# database = MySQLDatabase('peewee', **{'charset': 'utf8', 'use_unicode': True, 'host': 'localhost', 'port': 3306, 'user': 'root', 'password': ''})

"""将数据库连接改成peewee_async.MySQLDatabase"""

database = AsyncMySQLDatabase('peewee', **{'charset': 'utf8', 'use_unicode': True, 'host': 'localhost', 'port': 3306, 'user': 'root', 'password': '87260333as'})

class UnknownField(object):

def __init__(self, *_, **__): pass

class BaseModel(Model):

def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

"""将事务改成atomic_async"""

self.trans = database.atomic_async

"""添加一个Manager类"""

self.object = Manager(database)

class Meta:

database = database

# 下略……

peewee-async官方文档写得很简略,使用方法也不难,只是在peewee原有的基础上多加了一点嵌套操作。

比如,查询操作:(其中async/await是python3.5+异步操作的新特性,若不明白请自行查阅官方文档)

class MainHandler(RequestHandler):

"""使用async关键字标识异步操作"""

async def get(self):

user = User()

uname = self.get_argument('uname', 'j')

uid = self.get_argument('uid', 10002)

try:

"""使用await关键字等待阻塞操作"""

"""单条查询使用object.get()方法将原本的peewee操作包裹起来"""

row = await user.object.get(

User.select(User.uname).where(User.uid == uid)

)

"""多条查询"""

query = await user.object.execute(

User.select(User.uname).where(User.uname.startswith(uname))

)

except User.DoesNotExist:

"""当查不到数据的时候会抛出DoesNotExist错误"""

self.write('查无数据')

else:

self.write('row uname: %s and query unames:%s' % (row.uname, ', '.join([row.uname for row in query])))

启动进程,当我们再次锁住user表写锁,然后先后访问localhost:8000/和localhost:8000/test,就会发现,虽然第一个请求还在阻塞状态,第二个请求却已经成功看到屏幕上打印出来的"test here"文字。而当我们解锁user表之后,第一个请求也能顺利完成。

其余常规操作

插入/更新数据

async def post():

"""md5加密密码"""

m = hashlib.md5()

m.update('123456'.encode())

pwd = m.hexdigest()

"""头像"""

avartar = 'https://avatars2.githubusercontent.com/u/25029451'

user = User()

data_source = [

(avartar, 'Doreis', pwd),

(avartar, 'Dorein', pwd),

(avartar, 'Doreiv', pwd),

]

field = (User.avartar, User.uname, User.password)

try:

"""插入多条数据"""

await user.object.execute(

User.insert_many(data_source, field)

)

result = await user.object.get(User.select(User.uid, User.integral).where(User.uname == 'Doreis'))

"""使用async关键字开启事务"""

async with user.trans():

"""更新数据"""

await user.object.execute(User.update(

integral=result.integral+100

).where(User.uid == result.uid))

"""插入单条数据"""

await user.object.execute(Integral.insert(

uid=result.uid,

change=100,

total=result.integral + 100

))

self.write('insert complete')

except Exception as e:

print(repr(e))

self.write('insert failed')

jion查询

async def get(self):

user = User()

try:

"""join查询"""

query_join = await user.object.execute(

Balance.select(

User.uname, Balance.change,

Balance.total, Balance.addtime

).join(

User, JOIN.RIGHT_OUTER,

on=(Balance.uid == User.uid)

).where(

Balance.uid == 10012

).order_by(-Balance.addtime)

)

for row_join in query_join:

print('%s:%.2f:%.2f:%s' % (

row_join.user.uname, row_join.change,

row_join.total, row_join.addtime

))

except User.DoesNotExist:

"""当查不到数据的时候会抛出DoesNotExist错误"""

self.write('查无数据')

else:

self.write('query unames:%s' % (row.uname, ', '.join([row.uname for row in query])))

union查询

关于union操作,peewee_async原本并不支持。我开了个issue询问了作者,作者表示做出一些修改就可以支持。

Not sure if select() coroutine could handle that but you can try it peewee_async.select(union_query))

If it works, then we can just add handling ModelCompoundSelectQuery to execute() via select()

所以,首先,我们对peewee_async库的源文件peewee_async.py中的

execute()做出一点修改:

async def execute(query):

"""Execute *SELECT*, *INSERT*, *UPDATE* or *DELETE* query asyncronously.

:param query: peewee query instance created with ``Model.select()``,

``Model.update()`` etc.

:return: result depends on query type, it's the same as for sync

``query.execute()``

"""

"""在这里,添加ModelCompoundSelectQuery类"""

if isinstance(query, (peewee.Select, peewee.ModelCompoundSelectQuery)):

coroutine = select

elif isinstance(query, peewee.Update):

coroutine = update

elif isinstance(query, peewee.Insert):

coroutine = insert

elif isinstance(query, peewee.Delete):

coroutine = delete

else:

coroutine = raw_query

return (await coroutine(query))

这样在使用execute()方法时,peewee_async就能识别union。

"""union查询"""

b_query = (Balance.select(

Balance.b.alias('id'), Balance.change,

Balance.total, Balance.addtime

))

i_query = (Integral.select(

Integral.i.alias('id'), Integral.change,

Integral.total, Integral.addtime

))

query_union = await user.object.execute(

(b_query + i_query).order_by(SQL('addtime desc')).limit(2).offset(1)

)

聚合查询

"""聚合函数"""

fn_max = await user.object.scalar(User.select(fn.MAX(User.balance)))

删除操作

await user.object.execute(User.delete().where(User.uid == 10012))

简单的操作先记录到这里,其余操作未完待续~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用`tornado_mysql`的连接池时,可以使用以下代码来执行事务: ```python import tornado.ioloop import tornado.web import tornado.gen import tornado_mysql class MainHandler(tornado.web.RequestHandler): async def get(self): db = self.application.db async with db.pool.acquire() as conn: async with conn.cursor() as cur: try: await cur.execute("START TRANSACTION") await cur.execute("INSERT INTO table_name (column1, column2) VALUES (%s, %s)", (value1, value2)) await cur.execute("UPDATE table_name SET column3 = %s WHERE id = %s", (value3, id)) await cur.execute("COMMIT") self.write("Transaction completed successfully!") except: await cur.execute("ROLLBACK") self.write("Transaction failed") class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", MainHandler), ] self.db = tornado_mysql.Pool( dict(host='localhost', port=3306, user='root', passwd='password', db='database_name'), max_size=10, autocommit=True ) super(Application, self).__init__(handlers) if __name__ == "__main__": app = Application() app.listen(8888) tornado.ioloop.IOLoop.current().start() ``` 在上述代码中,我们首先创建了一个`Pool`对象,它包含了数据库的连接参数和连接池的最大连接数。然后,在我们的请求处理程序中,我们通过`pool.acquire()`方法获取一个连接。我们使用`async with`来确保在完成事务后自动释放该连接。 在使用的连接上创建一个`cursor`来执行事务中的SQL语句。在使用`try/except`块执行SQL语句时,如果任何一个语句失败,我们将立即回滚整个事务。如果所有语句都成功执行,则提交事务。 总之,使用`tornado_mysql`的连接池和事务非常简单,只需要在您的代码中添加一些额外的异步语句即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值