淺談Python peewee 使用經驗,pythonpeewee

來源:互聯網
上載者:User

淺談Python peewee 使用經驗,pythonpeewee

本文使用案例是基於 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:root@127.0.0.1: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 request.@app.teardown_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. 參考

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援幫客之家。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.