Tornado handlers
Torhandlers - it's a class based views (handlers, in tornado terms) inspired by great django's CBV's. Yes, I know about tornado-generic-handlers, but found them a bit limited for my purposes.
Handlers is developed to be used with SQLAlchemy, WTForms and jinja2. Templating system can be changed. In most cases, this handlers is similar to django's CBV's.
Installation
pip install torhandlers
Configuration
Handlers required a bit of configuration. Parameters usually specified in tornado's application settings dictionary. To use handlers with jinaj2 templating:
# application.py
from sqlalchemy.orm import scoped_session, sessionmaker
from database import engine
from urls import url_patterns
from torhandlers.templating import Jinja2Templater
class MyApp(tornado.web.Application):
def __init__(self, *args, **kwargs):
jinja2_settings = {
'autoescape': True,
'extensions': [
'jinja2.ext.with_'
],
}
jinja2_environment = Environment(
loader=FileSystemLoader(TEMPLATE_PATH),
undefined=StrictUndefined,
**jinja2_settings
)
settings = dict(
template_path=TEMPLATE_PATH,
static_path=STATIC_PATH,
debug=True,
xsrf_cookies=True,
jinja2_env=jinja2_environment,
templater=Jinja2Templater
)
super().__init__(url_patterns, *args, **dict(settings, **kwargs))
self.db = scoped_session(sessionmaker(bind=engine))
If you prefer default tornado templating system - import TornadoTemplater and use them:
# application.py
...
from torhandlers.templating import TornadoTemplater
class MyApp(tornado.web.Application):
def __init__(self, *args, **kwargs):
...
settings = dict(
...
templater=TornadoTemplater
)
super().__init__(url_patterns, *args, **dict(settings, **kwargs))
self.db = scoped_session(sessionmaker(bind=engine))
If you want to use Mako templating - create your own 'MakoTemplater' (and don't forget to make pull request!). Templater should implement only one method - render.
Basic usage
from models import Entry
from forms import addEntryForm
from torhandlers import (
TemplateHandler, ListHandler, DetailHandler,
CreateHandler, EditHandler, DeleteHandler
)
class IndexHandler(TemplateHandler):
template_name = 'index.html'
class EntryListHandler(ListHandler):
template_name = 'list.html'
model = Entry
paginate_by = 10
class EntryAddHandler(CreateHandler):
template_name = 'create.html'
model = Entry
form_class = addEntryForm
success_url_name = 'list'
class EntryDetailHandler(DetailHandler):
template_name = 'detail.html'
model = Entry
pk_url_kwarg = 'id'
class EntryEditHandler(EditHandler):
template_name = 'edit.html'
model = Entry
pk_url_kwarg = 'id'
form_class = addEntryForm
success_url_name = 'list'
class EntryDeleteHandler(DeleteHandler):
template_name = 'delete.html'
model = Entry
success_url_name = 'list'
pk_url_kwarg = 'id'
There is also handlers for creating dynamic apps (REST). They are pretty similar except that there is no need to specify template name. For complete examples see examples directory in this repository.
Decorators
In order to use Tornado's decorators - override the http method:
class EntryDetailHandler(DetailHandler):
template_name = 'detail.html'
model = Entry
pk_url_kwarg = 'id'
@tornado.web.authenticated
def get(self, *args, **kwargs):
return super().get(*args, **kwargs)
Extending
Usually, this handlers will be used among with another function (check permission, etc..). One of the valid approaches would be to create a mixin:
from tornado.web import HTTPError
class PermissionRequiredMixin:
def get_current_user(self):
# This is just a example, don't use in production!
username = self.get_secure_cookie("user")
if username:
return username
else:
return None
def initialize(self):
super().initialize()
if not self.get_current_user():
raise HTTPError(403)
def get_context_data(self, **kwargs):
kwargs['user'] = self.get_current_user()
return super().get_context_data(**kwargs)
class EntryDetailHandler(PermissionRequiredMixin, DetailHandler):
template_name = 'detail.html'
model = Entry
pk_url_kwarg = 'id'
Async
Right now those handlers not async by default. There is no simple way to create universal async handlers because this depends on async strategy in whole application.