tornado+peewee-async+peewee+mysql(一)

前言:
  • 须要异步操做MySQL,又要用orm,使用sqlalchemy须要加celery,以为比较麻烦,选择了peewee-async
开发环境 python3.6.8+peewee-async0.5.12+peewee2.10.2
  • 数据库:MySQL,使用peewee-async须要依赖库 pip install aiomysqlpython

  • peewee-async,对peewee版本只支持peewee<=2.10.2,>=2.8.0mysql

  • python3.5之后使用async和await关键字实现原生协程,也可使用tornado的gen模块下coroutine实现协程,或者asyncio模块实现协程,下文统一使用async和awaitweb

tornado 异步调用MySQL

  • 爬坑

    • 最初遇到的坑,使用了最新版的peewee,链接池链接,也使用了async和await协程,可是怎么调用都会阻塞,后来发现不是阻塞单个协程,是阻塞了整个进程,由于tornado是单进程,必须数据库也使用异步操做,才能不阻塞整个进程sql

    • pip install peewee-async 的时候默认会安装符合版本要求的peewee,想用最新的peewee模块可使用--pre数据库

    • 查看peewee-async 模块的MySQLDatabase,继承了AsyncDatabase和peewee.MySQLDatabase,AsyncDatabase方法所有使用协程实现异步json

  • peewee-async 链接MySQL,返回database对象

    • 单链接api

      import peewee_async
      # db = peewee_async.MySQLDatabase(database_name, host, port, user, password)
      #  或者,将本身的数据库信息封装到字典中
      db_setting = {
              "user": "root",
              "password": "xxxxxx",
              "host": "127.0.0.1",
              "port": 3306,
              "database": "test"
          }
      db = peewee_async.MySQLDatabase(**db_setting)
    • 链接池app

      from playhouse.shortcuts import RetryOperationalError
      from peewee_async import PooledMySQLDatabase
      #  能够自动从新链接的链接池
      class RetryMySQLDatabase(RetryOperationalError, PooledMySQLDatabase):
          _instance = None
      
          @staticmethod
          def get_db_instance():
              if not RetryMySQLDatabase._instance:
                  RetryMySQLDatabase._instance = RetryMySQLDatabase(database_name,
                                                                  host, port, user, password,
                                                                  max_connections=10)
              return RetryMySQLDatabase._instance
      
      db = RetryMySQLDatabase.get_db_instance()
  • 返回的database对象的一些经常使用方法

    • get_tables() 返回列表,当前数据库的全部表名异步

    • get_columns(table_name) 传参表名,返回列表,包含ColumnMetadata对象,字段信息async

    • create_tables()

      第一个参数为列表,包含要建立的表model
      第二个参数safe, 不传默认为False,建表的时候若是表已经存在会报错,能够加safe=True

    • is_closed() 判断当前链接是否关闭

    • close() 关闭链接

  • peewee

    • peewee 模块能够参照官方文档用法,和sqlalchemy差异不大,为了下面的操做,暂时建一个model

    book.py

    # 集中写一个basemodel,将数据库对象绑定在model上,类才能映射到数据库中的表
    class BaseModel(Model):
        class Meta:
            database = db
    class Book(BaseModel):
        book_id = PrimaryKeyField() # int 主键自增,在peewee3.10 版本中新增了字段AutoField,表示主键自增
        book_name = CharField(max_length=100, verbose_name="书名")
        # 鉴于篇幅, 做者表不写,外键第一个参数为model类名,to_field表示关联的字段
        book_auth = ForeignKeyField(User, to_field="user_id", verbose_name="做者id")
  • peewee-async Manager

    • 管理数据库操做,实现异步操做数据库必须使用Manager,查看源码能够看到,类中的get, create, execute等方法都是使用装饰器@asyncio.coroutine加yield from,在原有的数据库操做基础上作了封装
    • 初始化传入数据库链接对象,生成manager对象,使用该对象完成数据库操做,在tornado中通常选择绑定到app上
    from tornado.web import RequestHandler
    from tornado import gen
    import tornado.ioloop
    from book import Book
    
    class RegHandler(RequestHandler):
        async def get(self):
            # 在handler类中可使用绑定到app上的manager对象执行操做,由于是异步操做须要使用await关键字
            # 如下两种查询方式返回结果对象格式不一样
            book_res = await self.application.objects.get(Book, book_name="简爱")
            id = book_res.book_id
            # 只有调用了execute方法才是执行,query打印能够看到只是生成了sql语句
            # 为保证异步调用必须使用peewee-async manager生成的对象执行操做函数,不能使用model的execute执行,会同步阻塞
            query = Book.select().where(Book.username=="简爱")
            # execute执行返回AsyncQueryWrapper对象,若是有值能够经过下标取出每一个book对象
            # query 对象执行前能够调用dicts()方法,返回对象内容为字典格式
            # query.tuples() 返回对象内容为元组格式,至关于sqlalchemy,fetchall()
            # 其余方法或者属性有须要的可使用dir()方法和getattr()方法查看属性,以及属性调用后返回值
            book_res = await self.application.objects.execute(query.dicts())
            pass
        async def post(self):
            from tornado.escape import json_decode
            body = json_decode(self.request.body)
            # 增
            # 若是参数是字典格式,且key值对应字段名称,可使用peewee model里面的insert方法
            await self.application.objects.execute(Book.insert(body))
            # 或者使用封装的create方法,create方法源码仍是调用了model类的insert方法
            await self.application.objects.create(Book, boo_name=body.get("book"), book_auth=2)
            pass
    
    app = tornado.web.Application([
        (r"/book", BookHandler)
    ])
    
    if __name__ == '__main__':
        app = tornado.web.Application(urlpaten)
        import peewee_async
        # 将manager对象绑定到app上
        app.objects = peewee_async.Manager(database)
        server = httpserver.HTTPServer(app, xheaders=True)
        server.listen(80)
        tornado.ioloop.IOLoop.current().start()

  • 初步介绍先先写到这里,经过上述介绍使用peewee和peewee-async没有大问题,后面会经过详细功能具体详细介绍使用,细小的api建议看官方文档

  • 有问题欢迎指出,随时修正

相关文章
相关标签/搜索