gaetk2.handlers - WSGI Request Handlers

The gaetk2.handlers package aims to be the working horse on which you build your application. Instead of a monolytic approach like GAETK1 we work with mixins here. All of this is based on webapp2 request handlers. Basically you overwrite get() post(). In there you do a self.response.write('foo'). gaetk2 provides you with the convinience functions return_text() for simple replies and render() for rendering jinja2 templates.

BasicHandler provides basic functionality and template variables. JsonBasicHandler is specialized to produce JSON.

Usually you would use DefaultHandler for your public pages. This includes BasicHandler, MessagesMixin, AuthenticationReaderMixin. For JSON output you would use JsonBasicHandler based on JsonBasicHandler and AuthenticationReaderMixin.

If you want to ensure Users are authenticated use AuthenticatedHandler which extends DefaultHandler with AuthenticationRequiredMixin

Mix-Ins

Mix-Ins provide specialized functionality to handlers. They are mostly implemented using gaetk2.handlers.basic.BasicHandler Hook Mechanism. Some Mix-Ins provide just methods manually called by your method handlers.

gaetk2.handlers package

class gaetk2.handlers.DefaultHandler(*args, **kwargs)[source]

Handle Requests and load self.credential if Authentication is provided.

class gaetk2.handlers.JsonHandler(*args, **kwargs)[source]

Send JSON data to client and load self.credential if Authentication is provided.

class gaetk2.handlers.AuthenticatedHandler(*args, **kwargs)[source]
class gaetk2.handlers.AuthenticatedJsonHandler(*args, **kwargs)[source]
class gaetk2.handlers.base.BasicHandler(*args, **kwargs)[source]

Generic Handler functionality.

You usually overwrite get() or post() and call render() in there. See gaetk2.handlers for examples.

For Returning Data to the user you can access the self.response object or use return_text() and render(). See _create_jinja2env() to understand the jinja2 context being used.

Helper functions are abs_url() and is_production().

See also

is_sysadmin(), is_staff() and has_permission() are meant to work with AuthenticationReaderMixin

credential

authenticated user, see AuthenticationReaderMixin

session

current session which is based on https://github.com/dound/gae-sessions.

default_cachingtime

None or int – Class Variable. Which cache headers to generate, see _set_cache_headers().

debug_hooks

boolean – Class Variable. If set to True the order in which hooks are called is logged.

Note

gaetk2 adds various variables to the template context. Other mixins provide additional template variables. See the Index Index under “Template Context” to get an overview.

These Template Variables are provided:

  • request
  • credential
  • is_staff
  • is_sysadmin
  • gaetk_production
  • gaetk_development
  • gaetk_app_name
  • gaeth_version
  • gaetk_logout_url

Warning

BasicHandler implements a rather unusual way to implement Multi-Inherance/Mix-Ins. Instead of insisting that every parent class and everty Mix-In implements all possible methods and calls super on them BasicHandler uses a custom dispatch mechanism which calls all methods in all parent and sibling classes.

The following functions are called on all parent and sibling classes:

build_context() is special because the output is “chained”. So the rendering is done with something like the output of Child.build_context(Parent.build_context(MixIn.build_context({})))

response_overwrite() and finished_overwrite() can be overwritten to provide special functionality like in JsonBasicHandler.

You are encouraged to study the source code of BasicHandler!

abs_url(url)[source]

Converts an relative into an qualified absolute URL.

Parameters:url (str) – an path to a web resource.
Returns:A fully qualified url.
Return type:str

Example

>>> BasicHandler().abs_url('/foo')
'http://server.example.com/foo'
is_sysadmin()[source]

Checks if the current user is logged in as a SysOp/SystemAdministrator.

We use various souces to deterine the Status of the user. Returns True if:

  • google.appengine.api.users.is_current_user_admin()
  • the request came from 127.0.0.1 local address
  • self.credential.sysadmin == True
Returns:the status of the currently logged in user.
Return type:boolean
is_staff()[source]

Returns if the current user is considered internal.

This means he has access to not only his own but to all settings pages, etc.

Returns:the status of the currently logged in user is considered internal.
Return type:boolean
has_permission(permission)[source]

Checks if user has a given permission.

render(values, template_name)[source]

Render a Jinja2 Template and write it to the client.

If rendering takes an unusual long time this is logged.

Parameters:
  • values (dict) – variables for template context.
  • template_name (str) – name of the template to render.

See also

build_context()`also provides data to the template context and is often extended by plugins. See :class:`BasicHandler() docsting for standard template variables.

return_text(text, status=200, content_type='text/plain', encoding='utf-8')[source]

Quick and dirty sending of some plaintext to the client.

Parameters:
  • text (str or unicode) – Data to be sent to the cliend. A \n is appended.
  • status (int) – status code to be sent to the client. Defaults to 200.
  • content_type – to be sent to the client in respective header.
  • encoding – to be used when sending to the client.
build_context(values)[source]

Helper to provide additional request-specific values to HTML Templates.

Will be called on all Parents and MixIns, no super() needed.

def build_context(self, values):
myvalues = dict(navsection=’kunden’, …) myvalues.update(values) return myvalues
_add_jinja2env_globals(env)[source]

Helper to provide additional Globals to Jinja2 Environment.

This should be considered one time initialisation.

Example::
env.globals[‘bottommenuurl’] = ‘/admin/’ env.globals[‘bottommenuaddon’] = ‘<i class=”fa fa-area-chart”></i> Admin’ env.globals[‘profiler_includes’] = gae_mini_profiler.templatetags.profiler_includes
debug(message, *args)[source]

Detailed logging for development.

This logging will only happen, if WSGIApplication was initialized with debug=True. Is meant for local inspection of the stack during development. Messages are prefixed with the method name from where they are called.

pre_authentication_hook(method_name, *args, **kwargs)[source]

Might do redirects before even authentication data is loaded.

Called on all parent and sibling classes.

authentication_preflight_hook(method_name, *args, **kwargs)[source]

Might load Authentication data from Headers.

Called on all parent and sibling classes.

authentication_hook(method_name, *args, **kwargs)[source]

Might verify Authentication data.

Called on all parent and sibling classes.

authorisation_hook(method_name, *args, **kwargs)[source]

Might check if authenticated user is authorized.

Called on all parent and sibling classes.

method_preperation_hook(method_name, *args, **kwargs)[source]

Is Called just before GEP, POST, PUT, DELETE etc. is called.

Used to provide common data in child classes. E.g. to set up variables, load Date etc.

response_overwrite(response, method, *args, **kwargs)[source]

Function to transform response. To be overwritten.

finished_hook(method, *args, **kwargs)[source]

To be called at the end of an request.

finished_overwrite(response, method, *args, **kwargs)[source]

Function to allow logging etc. To be overwritten.

clear_session()[source]

Terminate the session reliably.

_create_jinja2env()[source]

Initialise and return a jinja2 Environment instance.

_jinja2_exception_handler(traceback)[source]

Is called during Jinja2 Exception processing to provide logging.

_render_to_fd(values, template_name, fd)[source]

Sends the rendered content of a Jinja2 Template to Output.

Per default the template is provided with output of build_context(values).

_set_cache_headers(caching_time=None)[source]

Set Cache Headers.

Parameters:caching_time (None or int) – the number of seconds, the result should be cachetd at frontend caches. None means no Caching-Headers. See also default_cachingtime. 0 or negative Values generate an comand to disable all caching.
_call_all_inherited(funcname, *args, **kwargs)[source]

In all SuperClasses call funcname - if it exists.

_reduce_all_inherited(funcname, initial)[source]

In all SuperClasses call funcname with the output of the previus call.

dispatch()[source]

Dispatches the requested method fom the WSGI App.

Meant for internal use by the stack.

handle_exception(exception, debug)[source]

Called if this handler throws an exception during execution.

The default behavior is to re-raise the exception to be handled by WSGIApplication.handle_exception().

Parameters:
  • exception – The exception that was thrown.
  • debug_mode – True if the web application is running in debug mode.
Returns:

response to be sent to the client.

class gaetk2.handlers.base.JsonBasicHandler(*args, **kwargs)[source]

Handler which is specialized for returning JSON.

Excepts the method to return

  • dict(), e.g. {‘foo’: bar}

Dict is converted to JSON. status is used as HTTP status code. cachingtime is used to generate a Cache-Control header. If cachingtime is None, no header is generated. cachingtime defaults to 60 seconds.

serialize(content)[source]

convert content to JSON.

response_overwrite(response, method, *args, **kwargs)[source]

Function to transform response. To be overwritten.

class gaetk2.handlers.mixins.paginate.PaginateMixin[source]

Show data in a paginated fashion.

Call paginate() in your Request-Method handler.

Example

Build a view function like this:

from ..handlers import AuthenticatedHandler
from ..handlers.mixins import PaginateMixin

class MyView(AuthenticatedHandler, PaginateMixin):
    def get(self):
        query = MyModel.query().order('-created_at)
        template_values = self.paginate(query)
        self.render(template_values, 'template.html')

Your template.html could look like this:

<ul>
  {% for obj in object_list %}
    <li>{{ obj }}</li>
  {% endfor %}
</ul>

{{ paginator4 }}

The {{ paginator4 }} expression renders a Bootstrap 4 Paginator object. If you dont want that you can add your own links:

{% if prev_objects %}
  <a href="?{{ prev_qs }}">&larr; Prev</a>
{% endif %}
{% if more_objects %}
  <a href="?{{ next_qs }}">Next &rarr;</a>
{% endif %}
paginate(query, defaultcount=25, datanodename='objects', calctotal=False, formatter=None)[source]

Add NDB-based pagination to Views.

Provides a template environment by calling self.paginate().

Parameters:
  • query – a ndb query object
  • defaultcount (int) – how many items to display per page
  • datanodename (string) – name of template variable to hold the entties
  • calctotal (boolean) – do you want to provide the total number of entities
  • formatter – function to transform entities for output.

formatter is called for each object and can transform it into something suitable. If no formatter is given and objects have a as_dict() method, this is used for formating.

if calctotal == True then the total number of matching rows is given as an integer value. This is a ecpensive operation on the AppEngine and results might be capped at 1000.

datanodename is the key in the returned dict, where the Objects resulting form the query resides.

defaultcount is the default number of results returned. It can be overwritten with the HTTP-parameter limit.

We handle the additional query parameters start, cursor, cursor_start from the HTTP-Request to note what is currently displayed. limit can be used to overwrite defaultcount.

The start HTTP-parameter can skip records at the beginning of the result set.

If the cursor HTTP-parameter is given we assume this is a cursor returned from an earlier query.

Returns:A dict like this to be used in the template:
{more_objects=True, prev_objects=True,
 prev_start=10, next_start=30, limit=10, total=None,
 objects=[...], cursor='ABCDQWERY',
 prev_qs='?start=10', next_qs='?start=30&cursor=ABCDQWERY',
 paginator='<html ...>'
}

paginator is generated by rendering gaetk_fragments/PaginateMixin.paginator.html with the other return values. You can overwrite the output by providing your own gaetk_fragments/PaginateMixin.paginator.html in your search path.

Note

Somewhat obsoleted by listviewer.

class gaetk2.handlers.mixins.messages.MessagesMixin[source]

MessagesMixin provides the possibility to send messages to the user.

Like Push-Notifications without the pushing.

add_message(typ, text, ttl=15)[source]

Sets a user specified message to be displayed to the currently logged in user.

typ can be error, success, info or warning text is the text do be displayed ttl is the number of seconds after we should stop serving the message.

If you want to pass in HTML, you need to use jinja2.Markup([string]).

build_context(uservalues)[source]

Add Messages to context.

class gaetk2.handlers.mixins.multirender.MultirenderMixin[source]

Provide rendering for a variety of formats with minimal code.

For the three major formats HTML, JSON, CSV and XML und you can get away with virtually no code.

Still nowadays we discourage the habit of massaging a single view into providing different formats of the same data.

multirender(fmt, data, mappers=None, contenttypes=None, filename='download', defaultfmt='html', html_template='data', html_addon=None, xml_root='data', xml_lists=None, tabular_datanodename='objects')[source]

Send Data formated in different ways to the client.

Some real-world view method might look like this:

# URL matches ‘/empfaenger/([A-Za-z0-9_-]+)/rechnungen.?(json|xml|html)?’, def get(self, kundennr, fmt):

query = models.Rechnung.all().filter(‘kundennr = ‘, kundennr) values = self.paginate(query, 25, datanodename=’rechnungen’) self.multirender(fmt, values,

filename=’rechnungen-%s’ % kundennr, html_template=’rechnungen.html’, tabular_datanodename=’rechnungen’)

/empfaenger/12345/rechnungen and /empfaenger/12345/rechnungen.html will result in rechnungen.html beeing rendered. /empfaenger/12345/rechnungen.json results in JSON being returned with a Content-Disposition header sending it to the file rechnungen-12345.json. Likewise for /empfaenger/12345/rechnungen.xml. If you add the Parameter disposition=inline no Content-Desposition header is generated.

If you use fmt=json with a callback parameter, JSONP is generated. See http://en.wikipedia.org/wiki/JSONP#JSONP for details.

If you give a dict in html_addon this dict is additionaly passed the the HTML rendering function (but not to the rendering functions of other formats).

You can give the xml_root and xml_lists parameters to provide dict2xml() with defenitions on how to name elements. See the documentation of roottag and listnames in dict2xml documentation.

For tabular formats like XLS and CSV we assume that data[tabular_datanodename] contains a list of dicts to be rendered.

For more sophisticated layout you can give customized mappers. Using functools.partial is very helpfiull for thiss. E.g.

from functools import partial multirender(fmt, values,

mappers=dict(xml=partial(dict2xml, roottag=’response’,
listnames={‘rechnungen’: ‘rechnung’, ‘odlines’: ‘odline’},
pretty=True),

html=lambda x: ‘<body><head><title>%s</title></head></body>’ % x))