Source code for django_utils.view_decorators

import six
import json
from django.template import loader as django_loader
from django import http
from django.contrib.auth import decorators
from django.core import serializers
from django.db import models
from django import urls


[docs]class ViewError(Exception): pass
[docs]class UnknownViewResponseError(ViewError): pass
[docs]def json_default_handler(obj): if hasattr(obj, 'isoformat'): return obj.isoformat() else: raise TypeError('Object of type %s with value of %s is not JSON ' 'serializable' % (type(obj), repr(obj)))
[docs]def redirect(url='./', *args, **kwargs): if '/' not in url or args or kwargs: url = urls.reverse(url, args=args, kwargs=kwargs) return http.HttpResponseRedirect(url)
[docs]def permanent_redirect(url, *args, **kwargs): if '/' not in url or args or kwargs: url = urls.reverse(url, args=args, kwargs=kwargs) return http.HttpResponsePermanentRedirect(url)
REQUEST_PROPERTIES = { 'redirect': redirect, 'permanent_redirect': permanent_redirect, 'not_found': http.HttpResponseNotFound, 'reverse': urls.reverse, } def _prepare_request(request, app, view): '''Add context and extra methods to the request''' request.context = dict() request.context['view'] = view request.context['app'] = app request.context['request'] = request for k, v in REQUEST_PROPERTIES.items(): setattr(request, k, v) return request def _process_response(request, response, response_class): '''Generic response processing function, always returns HttpResponse''' '''If we add something to the context stack, pop it after adding''' if isinstance(response, (dict, list, models.query.QuerySet)): if request.ajax: if isinstance(response, models.query.QuerySet): output = serializers.serialize('json', response) elif request.GET.get('debug'): from django.core.serializers import json as django_json output = json.dumps( response, indent=4, sort_keys=True, cls=django_json.DjangoJSONEncoder, default=json_default_handler, ) else: output = json.dumps(response, default=json_default_handler) callback = request.GET.get('callback', False) if callback: output = '%s(%s)' % (callback, output) if request.GET.get('debug'): title = 'Rendering %(view)r in module %(app)r' % ( request.context) output = ''' <html> <head> <title>%s</title> <style> textarea{ width: 100%%; height: 100%%; } </style> </head> <body> <textarea>%s</textarea> </body> </html> ''' % (title, output) response = response_class(output, content_type='text/html') else: response = response_class( output, content_type='text/plain') return response else: '''Add the dictionary to the context and let render_to_response handle it''' request.context.update(response) response = None if isinstance(response, http.HttpResponse): return response elif isinstance(response, six.string_types): if request.ajax: return response_class(response, content_type='text/plain') else: return response_class(response) elif response is None: render_to_string = django_loader.render_to_string return response_class(render_to_string( request.template, context=request.context, request=request)) else: raise UnknownViewResponseError( '"%s" is an unsupported response type' % type(response))
[docs]def env(function=None, login_required=False, response_class=http.HttpResponse): ''' View decorator that automatically adds context and renders response Keyword arguments: login_required -- is everyone allowed or only authenticated users Adds a RequestContext (request.context) with the following context items: name -- current function name Stores the template in request.template and assumes it to be in <app>/<view>.html ''' def _env(request, *args, **kwargs): request.ajax = bool(max( request.is_ajax(), int(request.POST.get('ajax', 0)), int(request.GET.get('ajax', 0)), )) request.context = None try: name = function.__name__ app = function.__module__.split('.')[0] request = _prepare_request(request, app, name) request.template = '%s/%s.html' % (app, name) response = function(request, *args, **kwargs) return _process_response(request, response, response_class) finally: '''Remove the context reference from request to prevent leaking''' try: del request.context, request.template for k in REQUEST_PROPERTIES.keys(): # pragma: no branch delattr(request, k) except AttributeError: pass # pragma: no branch if function: _env.__name__ = function.__name__ _env.__doc__ = function.__doc__ _env.__module__ = function.__module__ _env.__dict__ = function.__dict__ if login_required: return decorators.login_required(_env) else: return _env else: def inner(function): return env(function, login_required, response_class) return inner