torhandlers

Library with tornado framework handlers similar to Django's CBV's


Keywords
tornado, django, generic, json, ajax, rest
License
MIT
Install
pip install torhandlers==0.1.1

Documentation

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.