使用 python 3.3 和tornado, 其實也是我的興趣問題,而後數據庫也使用mysql, 這裏 使用oracle 自家的 mysql-connector. 天然,orm就選擇 sqlalchemy了. 模板引擎使用jinja2, form驗證考慮wtforms。 html
項目配置大概如上。以後可能使用全文檢索和數據庫逐步更新功能,所以又附加了 alembic 和 whoosh 倆個包。 python
先看下 入口 server.py mysql
#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()
爲了給tornado 實現 每一個handler 單獨配置url, git
相似這樣 github
@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後綴帶斜槓和不帶斜槓的處理 web
配置好的route 還能夠這樣用 sql
<a href="{{ reverse_url('login') }}">Login</a>