修改torndb库为依赖pymysql,使其适应python3,一个更简单的操作数据库的类。
1、python的MySQLdb和pymysql是两个基本数据库操作包,MySQLdb安装很麻烦,要有c++相关环境,python3也安装不了。
python3一般安装pymysql,此包与MySQLdb包具有高度的可替换性,只要学习一种库的api,另一种库的操作方法完全一模一样,不需要新学api。
但这两个都是偏底层,有个硬伤是公有查询方法不好用,因为执行是cursor.excute,取结果却要二次在游标上进行操作,所以一般情况下大部分人都会进行二次封装。
2、torndb就是一个这样的库,不需要调用者去关心数据库游标,每个公有方法都是直接返回结果,这也是一般情况下对数据库二次封装想达到的目标,有了torndb就不需要苦逼的自己去封装数据库操作了,就像有了好用的requests,70%场景下是不需要再去封装urllib了,也有特殊情况,我基于requests的Session类进行了二次封装,使其更容易使用,封装后在类外部只需要很少的外置代码,通过调用封装的方法就能得到结果了。
3、痛点就是torndb使依赖的是MySQLdb,安装难,而且里面有几个python语法在python2有,pyhton3去掉了,最后一次官方更新是2014年时候,估计是官方不打算更新来兼容py3了。
我把它修改成了依赖pymysql,兼容python3,并发布到pypi官网了。可以使用pip install torndb_for_python3来安装这个包,也可以自己复制以下代码。
torndb_for_python3完全兼容torndb包,api一模一样,torndb_for_python3包的使用方法,去百度搜索torndb的方法就可以了。
以下代码中有#TODO注释的行是在官方代码上修改了的行。
torndb_for_python3.py的代码。
#!/usr/bin/env python
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License. """A lightweight wrapper around MySQLdb. Originally part of the Tornado framework. The tornado.database module
is slated for removal in Tornado 3.0, and it is now available separately
as torndb.
官方torndb需要搭配MySQLdb包,主要是修改成搭配pymysql,有TODO注释的行都是在官方基础上修改了的,是为了兼容pymsql的参数和兼容python3 """ from __future__ import absolute_import, division, with_statement import copy
import itertools
import logging
import os
import time
import pymysql # TODO 需要安装pymysql pymysql.install_as_MySQLdb() # TODO 使用monkey技术将MySQLdb包替换为了pymsql包,所以不需要在代码里面大量替换MySQLdb为pymsql字眼,pymsql和MySQLdb的主要类是鸭子类的关系 try:
import MySQLdb.constants
import MySQLdb.converters
import MySQLdb.cursors
except ImportError:
# If MySQLdb isn't available this module won't actually be useable,
# but we want it to at least be importable on readthedocs.org,
# which has limitations on third-party modules.
if 'READTHEDOCS' in os.environ:
MySQLdb = None
else:
raise version = "0.3"
version_info = (0, 3, 0, 0) class Connection(object):
"""A lightweight wrapper around MySQLdb DB-API connections. The main value we provide is wrapping rows in a dict/object so that
columns can be accessed by name. Typical usage:: db = torndb.Connection("localhost", "mydatabase")
for article in db.query("SELECT * FROM articles"):
print article.title Cursors are hidden by the implementation, but other than that, the methods
are very similar to the DB-API. We explicitly set the timezone to UTC and assume the character encoding to
UTF-8 (can be changed) on all connections to avoid time zone and encoding errors. The sql_mode parameter is set by default to "traditional", which "gives an error instead of a warning"
(http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html). However, it can be set to
any other mode including blank (None) thereby explicitly clearing the SQL mode.
""" def __init__(self, host, database, user=None, password=None,
max_idle_time=7 * 3600, connect_timeout=30, # TODO 由0修改成了30,因为pymsql必须大于0
time_zone="+0:00", charset="utf8", sql_mode="TRADITIONAL"):
self.host = host
self.database = database
self.max_idle_time = float(max_idle_time) args = dict(conv=CONVERSIONS, use_unicode=True, charset=charset,
db=database, init_command=('SET time_zone = "%s"' % time_zone),
connect_timeout=connect_timeout, sql_mode=sql_mode)
if user is not None:
args["user"] = user
if password is not None:
args["passwd"] = password # We accept a path to a MySQL socket file or a host(:port) string
if "/" in host:
args["unix_socket"] = host
else:
self.socket = None
pair = host.split(":")
if len(pair) == 2:
args["host"] = pair[0]
args["port"] = int(pair[1])
else:
args["host"] = host
args["port"] = 3306 self._db = None
self._db_args = args
self._last_use_time = time.time()
try:
self.reconnect()
except Exception:
logging.error("Cannot connect to MySQL on %s", self.host,
exc_info=True) def __del__(self):
self.close() def close(self):
"""Closes this database connection."""
if getattr(self, "_db", None) is not None:
self._db.close()
self._db = None def reconnect(self):
"""Closes the existing database connection and re-opens it."""
self.close()
self._db = MySQLdb.connect(**self._db_args)
self._db.autocommit(True) def iter(self, query, *parameters, **kwparameters):
"""Returns an iterator for the given query and parameters."""
self._ensure_connected()
cursor = MySQLdb.cursors.SSCursor(self._db)
try:
self._execute(cursor, query, parameters, kwparameters)
column_names = [d[0] for d in cursor.description]
for row in cursor:
yield Row(zip(column_names, row))
finally:
cursor.close() def query(self, query, *parameters, **kwparameters):
"""Returns a row list for the given query and parameters."""
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
column_names = [d[0] for d in cursor.description]
# return [Row(itertools.izip(column_names, row)) for row in cursor] # TODO 修改了此行
return [Row(itertools.zip_longest(column_names, row)) for row in cursor]
finally:
cursor.close() def get(self, query, *parameters, **kwparameters):
"""Returns the (singular) row returned by the given query. If the query has no results, returns None. If it has
more than one result, raises an exception.
"""
rows = self.query(query, *parameters, **kwparameters)
if not rows:
return None
elif len(rows) > 1:
raise Exception("Multiple rows returned for Database.get() query")
else:
return rows[0] # rowcount is a more reasonable default return value than lastrowid,
# but for historical compatibility execute() must return lastrowid.
def execute(self, query, *parameters, **kwparameters):
"""Executes the given query, returning the lastrowid from the query."""
return self.execute_lastrowid(query, *parameters, **kwparameters) def execute_lastrowid(self, query, *parameters, **kwparameters):
"""Executes the given query, returning the lastrowid from the query."""
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
return cursor.lastrowid
finally:
cursor.close() def execute_rowcount(self, query, *parameters, **kwparameters):
"""Executes the given query, returning the rowcount from the query."""
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
return cursor.rowcount
finally:
cursor.close() def executemany(self, query, parameters):
"""Executes the given query against all the given param sequences. We return the lastrowid from the query.
"""
return self.executemany_lastrowid(query, parameters) def executemany_lastrowid(self, query, parameters):
"""Executes the given query against all the given param sequences. We return the lastrowid from the query.
"""
cursor = self._cursor()
try:
cursor.executemany(query, parameters)
return cursor.lastrowid
finally:
cursor.close() def executemany_rowcount(self, query, parameters):
"""Executes the given query against all the given param sequences. We return the rowcount from the query.
"""
cursor = self._cursor()
try:
cursor.executemany(query, parameters)
return cursor.rowcount
finally:
cursor.close() update = execute_rowcount
updatemany = executemany_rowcount insert = execute_lastrowid
insertmany = executemany_lastrowid def _ensure_connected(self):
# Mysql by default closes client connections that are idle for
# 8 hours, but the client library does not report this fact until
# you try to perform a query and it fails. Protect against this
# case by preemptively closing and reopening the connection
# if it has been idle for too long (7 hours by default).
if (self._db is None or
(time.time() - self._last_use_time > self.max_idle_time)):
self.reconnect()
self._last_use_time = time.time() def _cursor(self):
self._ensure_connected()
return self._db.cursor() def _execute(self, cursor, query, parameters, kwparameters):
try:
return cursor.execute(query, kwparameters or parameters)
except OperationalError:
logging.error("Error connecting to MySQL on %s", self.host)
self.close()
raise def __str__(self):
return f'{type(self)} -> [{self.host}机器的{ self.database}库]' # TODO class Row(dict):
"""A dict that allows for object-like property access syntax.""" def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name) if MySQLdb is not None:
# Fix the access conversions to properly recognize unicode/binary
FIELD_TYPE = MySQLdb.constants.FIELD_TYPE
FLAG = MySQLdb.constants.FLAG
CONVERSIONS = copy.copy(MySQLdb.converters.conversions) field_types = [FIELD_TYPE.BLOB, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING]
if 'VARCHAR' in vars(FIELD_TYPE):
field_types.append(FIELD_TYPE.VARCHAR) for field_type in field_types:
# CONVERSIONS[field_type] = [(FLAG.BINARY, str)] + CONVERSIONS[field_type] # TODO 修改了此行
CONVERSIONS[field_type] = [(FLAG.BINARY, str)].append(CONVERSIONS[field_type]) # Alias some common MySQL exceptions
IntegrityError = MySQLdb.IntegrityError
OperationalError = MySQLdb.OperationalError
使用用法,举一个例子:
# pip install torndb_for_python3
mysql_conn = torndb_for_python3.Connection(host='localhost', database='test', user='root', password='123456', charset='utf8')
print(mysql_conn.query('SELECT * FROM test.tablexx'))
修改torndb库为依赖pymysql,使其适应python3,一个更简单的操作数据库的类。的更多相关文章
- 修改so库中的依赖名
修改so库中的依赖名 在ArchLinuxArm上有一些针对aarch64, arm, armeabi-v7a等Android常用架构的so库可以下载,有时候可以省去很多编译时间,且都是编译optim ...
- 修改nw.js的exe文件使其请求管理员权限
修改nw.js的exe文件使其请求管理员权限 默认情况下,nw.js发布的nw.exe文件请求的是普通权限,当我们的应用需要访问一些特殊目录或者注册表等,就需要程序启动的时候以管理员权限运行.那么此时 ...
- Linux 下动态库 / 静态库(依赖)
一. 依赖动态库的动态库 libfun.so依赖动态库libtest.so(libfun.so动态库里的函数intnothing()调用了libtest.so里的intmytest()函数),而mai ...
- Adobe Edge Animate –修改Edge Commons Spotlight功能,使之能支持播放中国网站视频
Adobe Edge Animate –修改Edge Commons Spotlight功能,使之能支持播放中国网站视频 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权 ...
- 如何对 Android 库进行依赖管理?
Android 开发人员为项目选择库的时候,考虑的因素不仅仅是功能.可用性.性能.文档丰富度和技术支持情况.他们还关心库的大小,以及要添加的方法数量.因为项目越大,依赖也越多,要把应用的方法数量控制在 ...
- golang 标准库间依赖的可视化展示
简介 国庆看完 << Go 语言圣经 >>,总想做点什么,来加深下印象.以可视化的方式展示 golang 标准库之间的依赖,可能是一个比较好的切入点.做之前,简单搜了下相关的内 ...
- Linux库多重依赖
源文件: //world.cpp #include <stdio.h> void world(void) { printf("world.\n"); } //hello ...
- Linux下一个最简单的不依赖第三库的的C程序(2)
一个最简单的C程序,如下: main.c: int main() { char *str = "Hello World"; ; } 在64位平台上编译一个32位的程序,如下:(32 ...
- Linux下一个最简单的不依赖第三库的的C程序(1)
如下代码是一段汇编代码,虽然标题中使用了C语言这个词语,但下面确实是一段汇编代码,弄清楚了这个代码,后续的知识点才会展开. simple_asm.s: #PURPOSE: Simple program ...
随机推荐
- tcp nonblock connection rst
客户端(>5w)异步connect连接到server端,server端listen backlog设置为1024,发现存在部分客户端建立连接后,收到服务端的rst包. 先看下tcp监听套接字维护 ...
- 数据结构C语言版--动态顺序表的基本功能实现(二)
/* * 若各个方法结构体变量参数为: &L(即地址符加变量)则结构体变量访问结构成员变量时使用"." * 若为:*L(即取地址符加变量)则结构体变量访问结构体成员变量使用 ...
- 哪个中年IT男不是一边面对危机,一边咬牙硬抗【转】
来自: 懂懂笔记 对于 2017 年年末那则令人哀伤的消息,相信很多同龄人都会触目伤怀.面对公司的强制性劝退,深圳中兴网信科技有限公司某研发组主管从办公楼上一跃而下,用最决绝的方式结束了宝贵的生命. ...
- bat遍历目录
方法一: for /r D:\要遍历的目录\ %%i (*.exe) do ( echo %%i ) 方法二: set DestPath=D:\你的目录\ rem 你的后缀 set Dest ...
- mysql 时间类型精确到毫秒、微秒及其处理
一.MySQL 获得毫秒.微秒及对毫秒.微秒的处理 MySQL 较新的版本中(MySQL 6.0.5),也还没有产生微秒的函数,now() 只能精确到秒. MySQL 中也没有存储带有毫秒.微秒的日期 ...
- CentOS7下FTP的安装与配置
1.安装vsftpd 1 [root@localhost modules]# yum install -y vsftpd 2.编辑ftp配置文件 1 [root@localhost modules]# ...
- [Unit Testing] Using Mockito Annotations - @Mock, @InjectMocks, @RunWith
Previously we have seen how to do Unit testing with Mockito; import org.junit.Test; import static or ...
- 浅谈压缩感知(二十一):压缩感知重构算法之正交匹配追踪(OMP)
主要内容: OMP的算法流程 OMP的MATLAB实现 一维信号的实验与结果 测量数M与重构成功概率关系的实验与结果 稀疏度K与重构成功概率关系的实验与结果 一.OMP的算法流程 二.OMP的MATL ...
- JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头
一.简介 JavaCV使用来自计算机视觉领域(OpenCV, FFmpeg, libdc1394, PGR FlyCapture, OpenKinect, librealsense, CL PS3 E ...
- Reg 命令修改注册表
首先要说明:编辑注册表不当可能会严重损坏您的系统.在更改注册表之前,应备份计算机上任何有价值的数据 只有在别无选择的情况下,才直接编辑注册表.注册表编辑器会忽略标准的安全措施,从而使得这些设置会降低性 ...