环境配置
使用 python 3.3 和tornado, 其实也是个人兴趣问题,然后数据库也使用mysql, 这里 使用oracle 自家的 mysql-connector. 自然,orm就选择 sqlalchemy了. 模板引擎使用jinja2, form验证考虑wtforms。
项目配置大概如上。之后可能使用全文检索和数据库逐步更新功能,因此又附加了 alembic 和 whoosh 俩个包。
入口
先看下 入口 server.py
#coding=utf-8
import tornado.ioloop
from tornado.options import define, options
from lib.base import MainApplication
from lib.routes import make_handlers, include
from settings import URL_PREFIX
# Options
#define("port", default=8888, help="run on the given port", type=int)
#define("db_path", default='sqlite:tmp/test.db', type=str)
settings = {
"template_path": "template",
"static_path": "static",
"debug": True,
#"logging": "debug",
"login_url": '/login',
"cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
"xsrf_cookies": True,
}
#application = MainApplication([#tornado.web.Application([
# (r"/", MainHandler),
#],**settings)
application = MainApplication(make_handlers(URL_PREFIX,
(r'/', include('handlers.index')),
(r'/', include('handlers.user')),
(r'/', include('handlers.userGroup')),
), **settings)
if __name__ == "__main__":
application.listen(8888)
tornado.options.options.logging = "debug"
tornado.options.parse_command_line()
tornado.ioloop.IOLoop.instance().start()
ROUTE
为了给tornado 实现 每个handler 单独配置url,
类似这样
@route('login/', name='login')
class LoginHandler(BaseHandler):
@tornado.web.asynchronous
def get(self):
self.render("login.html")
这里自己建了一个routes.py 参考
https://github.com/troolee/tornado-routes
#coding=utf-8
from pprint import pformat
import re
import logging
from tornado import web
from tornado.web import URLSpec
logger = logging.getLogger(__name__)
__ALL__ = ('make_handlers', 'include', 'route', 'routes', )
def handler_repr(cls):
return re.search("'(.+)'", repr(cls)).groups()[0]
class HandlersList(object):
def __init__(self, prefix, items):
self.prefix = prefix
self.items = items
def get_handler_name(self, handler, r):
name = getattr(handler, 'url_name', None)
if name:
return name
if hasattr(handler, 'get_url_name'):
name = handler.get_url_name(*r)
if name:
return name
if len(r) == 3 and 'url_name' in r[2]:
name = r[2].pop('url_name')
if name:
return name
return handler_repr(handler)
def build(self, prefix=None):
prefix = prefix or self.prefix or ''
res = []
for r in self.items:
print(r)
route = '/' + '/'.join([prefix.strip('/')] + r[0].strip('/').split('/')).strip('/')
print(route)
if isinstance(r[1], HandlersList):
res += r[1].build(route)
elif isinstance(r[1], str):
m = r[1].split('.')
ms, m, h = '.'.join(m[:-1]), m[-2], m[-1]
m = __import__(ms, fromlist=[m], level=0)
handler = getattr(m, h)[0]
d = {'name': self.get_handler_name(handler, r)}
#d.update(r[2:])
d['kwargs'] = {}
if len(r) == 3:
d['kwargs'] = r[2]
res.append(URLSpec(route, handler, **d))
if len(route) > 1:
#d['kwargs']['url'] = route;
d.pop('name')
res.append(URLSpec(route + '/', handler, **d))
else:
handler = r[1:][0]
d = {'name': self.get_handler_name(handler, r)}
d['kwargs'] = {}
if len(r) == 3:
d['kwargs'] = r[2]
res.append(URLSpec(route, handler, **d))
if len(route) > 1:
#d['kwargs']['url'] = route;
d.pop('name')
res.append(URLSpec(route + '/', handler, **d))
return res
def make_handlers(prefix, *args):
res = tuple(HandlersList(prefix, args).build())
rr = [(x.regex.pattern, x.handler_class, x.kwargs, x.name) for x in res]
logger.debug('\n' + pformat(sorted(rr, key=lambda a: a[0]), width=200))
return res
def include(module):
def load_module(m):
m = m.split('.')
ms, m = '.'.join(m), m[-1]
m = __import__(ms, fromlist=[m], level=0)
return m
if isinstance(module, (str,)):
module = load_module(module)
routes = []
for member in dir(module):
member = getattr(module, member)
if isinstance(member, type) and issubclass(member, web.RequestHandler) and hasattr(member, 'routes'):
i = 1
for route_path, route_params in member.routes:
route_path.strip('/')
if not route_params:
route_params = {}
if 'url_name' not in route_params:
route_params['url_name'] = '%s~%d' % (handler_repr(member), i)
routes.append((route_path, member, route_params))
i += 1
elif isinstance(member, type) and issubclass(member, web.RequestHandler) and hasattr(member, 'route_path'):
route_path, route_params = member.route_path, member.route_params
route_path.strip('/')
if route_params:
routes.append((route_path, member, route_params))
else:
routes.append((route_path, member))
elif isinstance(member, type) and issubclass(member, web.RequestHandler) and hasattr(member, 'rest_route_path'):
route_path, route_params = member.rest_route_path, member.route_params
route_path.strip('/')
if route_params:
routes.append((route_path, member, route_params))
routes.append((route_path + r'/([0-9]+)', member, route_params))
else:
routes.append((route_path, member))
routes.append((route_path + r'/([0-9]+)', member))
return HandlersList(None, routes)
route_classes = {}
def route(path, params=None, name=None):
params = params or {}
def decorator(cls):
if repr(cls) in route_classes:
raise Exception('Cannot bind route "%s" to %s. It already has route to "%s".' %
(path, cls, route_classes[repr(cls)]))
route_classes[repr(cls)] = path
cls.route_path = path
cls.route_params = params
url_name = params.pop('url_name', name)
cls.url_name = url_name
return cls
return decorator
def routes(*routes):
def decorator(cls):
cls.routes = routes
return cls
return decorator
同时支持url后缀带斜杠和不带斜杠的处理
配置好的route 还可以这样用
<a href="{{ reverse_url('login') }}">Login</a>