版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jackyzhousales/article/details/82354669
目前Tornado中的torndb模块是不支持python3.x,所以需要修改部分torndb源码即可正常使用
1、开发环境介绍
操作系统:Ubuntu16.04(64位),python版本:python3.6.1(64位),IDE:Sublime
2、安装torndb(这里使用pip进行安装)
pip install torndb
3、源码修改
sudo vim /project/venv/lib/python3.6/site-packages/torndb.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.
"""
from __future__ import absolute_import, division, with_statement
import copy
import itertools
import logging
import os
import time
try:
# import MySQLdb.constants
# import MySQLdb.converters
# import MySQLdb.cursors
import pymysql.connections
import pymysql.converters
import pymysql.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
pymysql = 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=10,
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 = pymysql.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]
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
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 pymysql is not None:
# Fix the access conversions to properly recognize unicode/binary
#FIELD_TYPE = MySQLdb.constants.FIELD_TYPE
FIELD_TYPE = pymysql.constants.FIELD_TYPE
# FLAG = MySQLdb.constants.FLAG
FLAG = pymysql.constants.FLAG
#CONVERSIONS = copy.copy(MySQLdb.converters.conversions)
CONVERSIONS = copy.copy(pymysql.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]
CONVERSIONS[field_type] = [(FLAG.BINARY, str)].append(CONVERSIONS[field_type])
# Alias some common MySQL exceptions
#IntegrityError = MySQLdb.IntegrityError
IntegrityError = pymysql.IntegrityError
#OperationalError = MySQLdb.OperationalError
OperationalError = pymysql.OperationalError
修改MySQLdb,torndb是依赖于MySQLdb实现的对MySQL数据库操作,但是python3中不支持MySQLdb,而是使用pymysql,所以需要将源码中使用MySQLdb的地方修改为pymysql。
1)修改导入模块
import pymysql.connections
import pymysql.converters
import pymysql.cursors
# import MySQLdb.constants
# import MySQLdb.converters
# import MySQLdb.cursors
2)修改连接mysql的方式
def reconnect(self):
"""Closes the existing database connection and re-opens it."""
self.close()
self._db = pymysql.connect(**self._db_args)# MySQLdb.connect(**self._db_args)
self._db.autocommit(True)
3)修改连接参数,以及遍历字段类型时所使用的列表增加元素(python3使用append进行元素的添加,而不是使用加号)
# if MySQLdb is not None:
if pymysql is not None:
# Fix the access conversions to properly recognize unicode/binary
FIELD_TYPE = pymysql.connections.FIELD_TYPE # MySQLdb.constants.FIELD_TYPE
FLAG = pymysql.constants.FLAG# MySQLdb.constants.FLAG
CONVERSIONS = copy.copy (pymysql.converters.conversions)# (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]
CONVERSIONS[field_type] = [(FLAG.BINARY, str)].append(CONVERSIONS[field_type])
# Alias some common MySQL exceptions
IntegrityError = pymysql.IntegrityError# MySQLdb.IntegrityError
OperationalError = pymysql.OperationalError# MySQLdb.OperationalError
4) 修改连接超时时间,在torndb初始化方法中设置,需要传递给pymysql
def __init__(self, host, database, user=None, password=None,
max_idle_time=7 * 3600, connect_timeout=10,# 设置连接超时时间,时间是秒
time_zone="+0:00", charset = "utf8", sql_mode="TRADITIONAL"):
5)修改查询方法中的迭代方法(将izip改为zip_longest)
扫描二维码关注公众号,回复:
3510350 查看本文章
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.zip_longest(column_names, row)) for row in cursor]
finally:
cursor.close()
4. python3源码
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import json
import torndb
import pymysql
import os
from tornado.options import define,options
from tornado.web import RequestHandler,url,StaticFileHandler
tornado.options.define("port",type=int,default=8000,help="服务端口")
class BaseHandler(RequestHandler):
def initialize(self):
print("调用了initialize()")
def prepare(self):
print("调用了prepare()")
def set_default_headers(self):
print("调用了set_default_headers()")
def write_error(self, status_code, **kwargs):
print("调用了write_error()")
def get(self):
print("调用了get()")
def post(self):
print("调用了post()")
self.send_error(200)
def on_finish(self):
print("调用了on_finish()")
class IndexHandler(BaseHandler):
def get(self):
#self.write("aaa")
ret = self.application.db.get("select ui_name from ops_user_info where ui_user_id=1")
self.write(ret["ui_name"])
if __name__ == '__main__':
tornado.options.parse_command_line()
current_path = os.path.dirname(__file__)
settings = dict(
static_path = os.path.join(current_path,"static"),
template_path = os.path.join(current_path,"template"),
debug=True,
autoescape=None
)
app = tornado.web.Application(
[
(r"/",IndexHandler),
(r"/(.*)", StaticFileHandler, {"path":os.path.join(current_path, "statics/html"),"default_filename":"index.html"}),
],**settings
)
app.db = torndb.Connection(
host="127.0.0.1",
database="jackyops",
user="root",
password="123456"
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()