侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

浅谈Python peewee 使用经验

Python  /  管理员 发布于 7年前   332

本文使用案例是基于 python2.7 实现

以下内容均为个人使用 peewee 的经验和遇到的坑,不会涉及过多的基本操作。所以,没有使用过 peewee,可以先阅读文档

正确性和覆盖面有待提高,如果遇到新的问题欢迎讨论。

一、介绍

Peewee 是一个简单、轻巧的 Python ORM。

  1. 简单、轻巧、富有表现力(原词 expressive )的ORM
  2. 支持python版本 2.6+ 和 3.2+
  3. 支持数据库包括:sqlite, mysql and postgresql
  4. 包含一堆实用的扩展在 playhouse 模块中

总而言之,peewee 可以完全可以应付个人或企业的中小型项目的 Model 层,上手容易,功能很强大。

二、基本使用方法

from peewee import *db = SqliteDatabase('people.db')class BaseModel(Model):  class Meta:    database = db # This model uses the "people.db" database.class Person(BaseModel):  name = CharField()  birthday = DateField()  is_relative = BooleanField()  

基本的使用方法,推荐阅读文档--quickstart

三、推荐使用姿势

下面介绍一些我在使用过程的经验和遇到的坑,希望可以帮助大家更好的使用 peewee。

3.1 连接数据库

连接数据库时,推荐使用 playhouse 中的 db_url 模块。db_url 的 connect 方法可以通过传入的 URL 字符串,生成数据库连接。

3.1.1 connect(url, **connect_params)

通过传入的 url 字符串,创建一个数据库实例

url形如:

  1. mysql://user:passwd@ip:port/my_db 将创建一个 本地 MySQL 的 my_db 数据库的实例(will create a MySQLDatabase instance)
  2. mysql+pool://user:passwd@ip:port/my_db?charset=utf8&max_connections=20&stale_timeout=300 将创建一个本地 MySQL 的 my_db 的连接池,最大连接数为20(In a multi-threaded application, up to max_connections will be opened. Each thread (or, if using gevent, greenlet) will have it's own connection. ),超时时间为300秒(will create a PooledMySQLDatabase instance)

注意:charset 默认为utf8。如需要支持 emoji ,charset 设置为utf8mb4,同时保证创建数据库时的字符集设置正确CREATE DATABASE mydatabase CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。

支持的 schemes:

  1. apsw: APSWDatabase
  2. mysql: MySQLDatabase
  3. mysql+pool: PooledMySQLDatabase
  4. postgres: PostgresqlDatabase
  5. postgres+pool: PooledPostgresqlDatabase
  6. postgresext: PostgresqlExtDatabase
  7. postgresext+pool: PooledPostgresqlExtDatabase
  8. sqlite: SqliteDatabase
  9. sqliteext: SqliteExtDatabase
  10. sqlite+pool: PooledSqliteDatabase
  11. sqliteext+pool: PooledSqliteExtDatabase

3.1.2 推荐姿势

from playhouse.db_url import connectfrom dock.common import config# url: mysql+pool://root:[email protected]:3306/appmanage?max_connections=300&stale_timeout=300mysql_config_url = config_dict.get('config').get('mysql').get('url')db = connect(url=mysql_config_url)

查看更多详情请移步官方文档:db-url

3.2 连接池的使用

peewee 的连接池,使用时需要显式的关闭连接。下面先说下为什么,最后会给出推荐的使用方法,避免进坑。

3.2.1 为什么要显式的关闭连接

Connections will not be closed exactly when they exceed their stale_timeout. Instead, stale connections are only closed when a new connection is requested.

这里引用官方文档的提示。大致说:“超时连接不会自动关闭,只会在有新的请求时是才会关闭”。这里的request是指‘web 框架处理的请求',peewee 源码片段:

def _connect(self, *args, **kwargs):  while True:    try:      # Remove the oldest connection from the heap.      ts, conn = heapq.heappop(self._connections) # _connections是连接实例的list(pool)      key = self.conn_key(conn)    except IndexError:      ts = conn = None      logger.debug('No connection available in pool.')      break    else:      if self._is_closed(key, conn):        # This connecton was closed, but since it was not stale        # it got added back to the queue of available conns. We        # then closed it and marked it as explicitly closed, so        # it's safe to throw it away now.        # (Because Database.close() calls Database._close()).        logger.debug('Connection %s was closed.', key)        ts = conn = None        self._closed.discard(key)      elif self.stale_timeout and self._is_stale(ts):        # If we are attempting to check out a stale connection,        # then close it. We don't need to mark it in the "closed"        # set, because it is not in the list of available conns        # anymore.        logger.debug('Connection %s was stale, closing.', key)        self._close(conn, True)        self._closed.discard(key)        ts = conn = None      else:        break  if conn is None:    if self.max_connections and (        len(self._in_use) >= self.max_connections):      raise ValueError('Exceeded maximum connections.')    conn = super(PooledDatabase, self)._connect(*args, **kwargs)    ts = time.time()    key = self.conn_key(conn)    logger.debug('Created new connection %s.', key)  self._in_use[key] = ts # 使用中的数据库连接实例dict  return conn

根据 pool 库中的 _connect 方法的代码可知:每次在建立数据库连接时,会检查连接实例是否超时。但是需要注意一点:使用中的数据库连接实例(_in_use dict中的数据库连接实例),是不会在创建数据库连接时,检查是否超时的。

因为这段代码中,每次创建连接实例,都是在 _connections(pool) 取实例,如果有的话就判断是否超时;如果没有的话就新建。

然而,使用中的数据库连接并不在 _connections 中,所以每次创建数据库连接实例时,并没有检测使用中的数据库连接实例是否超时。

只有调用连接池实例的 _close 方法。执行这个方法后,才会把使用后的连接实例放回到 _connections (pool)。

def _close(self, conn, close_conn=False):  key = self.conn_key(conn)  if close_conn:    self._closed.add(key)    super(PooledDatabase, self)._close(conn) # 关闭数据库连接的方法  elif key in self._in_use:    ts = self._in_use[key]    del self._in_use[key]    if self.stale_timeout and self._is_stale(ts):  # 到这里才会判断_in_use中的连接实例是否超时      logger.debug('Closing stale connection %s.', key)      super(PooledDatabase, self)._close(conn)  # 超时的话,关闭数据库连接    else:      logger.debug('Returning %s to pool.', key)      heapq.heappush(self._connections, (ts, conn)) # 没有超时的话,放回到pool中

3.2.2 如果不显式的关闭连接,会出现的问题

如果不调用_close方法的话,使用后 的数据库连接就一直不会关闭(两个含义:回到pool中和关闭数据库连接),这样会造成两个问题:

1.每次都是新建数据库连接,因为 pool 中没有数据库连接实例。会导致稍微有一点并发量就会返回Exceeded maximum connections.错误

2.MySQL也是有 timeout 的,如果一个连接长时间没有请求的话,MySQL Server 就会关闭这个连接,但是,peewee的已建立(后面会解释为什么特指已建立的)的连接实例,并不知道 MySQL Server 已经关闭了,再去通过这个连接请求数据的话,就会返回 Error 2006: “MySQL server has gone away”错误,根据官方文档

3.2.3 推荐姿势

所以,每次操作完数据库就关闭连接实例。

用法1:使用with

def send_rule():  with db.execution_context():  # A new connection will be opened or, if using a connection pool,  # pulled from the pool of available connections. Additionally, a  # transaction will be started.    for user in get_all_user():      user_id = user['id']      rule = Rule(user_id)      rule_dict = rule.slack_rule(index)      .....do something.....

用法2:使用Flask hook

@app.before_requestdef _db_connect():  database.connect()## This hook ensures that the connection is closed when we've finished# processing the [email protected]_requestdef _db_close(exc):  if not database.is_closed():    database.close()### 更优雅的用法:from playhouse.flask_utils import FlaskDBfrom dock_fastgear.model.base import db#app = Flask(__name__)FlaskDB(app, db) # 这样就自动做了上面的事情(具体实现可查看http://docs.peewee-orm.com/en/latest/peewee/playhouse.html?highlight=Flask%20DB#flask-utils)

查看更多详情请移步官方文档:pool-apis

3.3 处理查询结果

这里没有什么大坑,就是有两点需要注意:

首先,查询的结果都是该 Model 的 object,注意不是 dict。如果想让结果为 dict,需要 playhouse 模块的工具方法进行转化:from playhouse.shortcuts import model_to_dict

其次,get方法只会返回一条记录

3.3.1 推荐姿势

from playhouse.shortcuts import model_to_dictfrom model import HelloGitHubdef read_from_db(input_vol):  content_list = []  category_object_list = HelloGitHub.select(HelloGitHub.category).where(HelloGitHub.vol == input_vol)\    .group_by(HelloGitHub.category).order_by(HelloGitHub.category)  for fi_category_object in category_object_list:    hellogithub = HelloGitHub.select()\      .where((HelloGitHub.vol == input_vol)          & (HelloGitHub.category == fi_category_object.category))\      .order_by(HelloGitHub.create_time)    for fi_hellogithub in hellogithub:      content_list.append(model_to_dict(fi_hellogithub))  return content_list

四、常见错误及解决办法

4.1 'buffer' object has no attribute 'translate'

  1. 错误信息: "'buffer' object has no attribute 'translate'"
  2. 场景:BlobField 字段存储zlib compress压缩的数据
  3. 解决办法:需要指定pymysql的版本小于0.6.7 否则会报错
  4. 参考

4.2 Can't connect to MySQL server Lost connection to MySQL server during query

  1. 错误信息:Can't connect to MySQL server Lost connection to MySQL server during query
  2. 场景:向 RDS 中插入数据
  3. 解决办法:因为请求的连接数过多,达到了 RDS 设置的连接数,所以需要调高 RDS 连接数
  4. 参考

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    python中的计时器timeit的使用方法
    下一条:
    Python 获得13位unix时间戳的方法
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客