Package: django-conneg
Content-negotiation framework for Django========================================This project provides a simple and extensible framework for producing viewsthat content-negotiate in Django.Prerequisites-------------This library depends on Django 1.3, which you can install using your packagemanager on recent distributions, or using pip::pip install -r requirements.txt``pip`` is called ``pip-python`` on Fedora. It is generally provided by a``python-pip`` package.Using-----To define a view, do something like this::from django_conneg.views import ContentNegotiatedViewclass IndexView(ContentNegotiatedView):def get(self, request):context = {# Build context here}# Call render, passing a template name (without file extension)return self.render(request, context, 'index')This will then look for a renderer that can provide a representation thatmatches what was asked for in the Accept header.By default ContentNegotiatedView provides no renderers, so the above snippetwould always return a 405 Not Acceptable to tell the user-agent that itcouldn't provide a response in a suggested format.To define a renderer on a view, do something like this::import jsonfrom django.http import HttpResponsefrom django_conneg.decorators import rendererclass JSONView(ContentNegotiatedView):@renderer(format='json', mimetypes=('application/json',), name='JSON')def render_json(self, request, context, template_name):# Very simplistic, and will fail when it encounters 'non-primitives'# like Django Model objects, Forms, etc.return HttpResponse(json.dumps(context), mimetype='application/json').. note::``django-conneg`` already provides a slightly more sophisticated JSONView;see below for more information.You can render to a particular format by calling ``render_to_format()`` on theview::class IndexView(ContentNegotiatedView):def get(self, request):# ...if some_condition:return self.render_to_format(request, context, 'index', 'html')else:return self.render(request, context, 'index')Forcing a particular renderer from the client~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~By default, a client can request a particular set of renderers be tried byusing the ``format`` query or POST parameter::GET /some-view/?format=json,yamlThe formats correspond to the ``format`` argument to the ``@renderer``decorator.To change the name of the parameter used, override``_format_override_parameter`` on the view class::class MyView(ContentNegotiatedView):_format_override_parameter = 'output'Providing fallback renderers~~~~~~~~~~~~~~~~~~~~~~~~~~~~Sometimes you might want to provide a response in some format even if thethose in the Accept header can't be honoured. This is useful when providingerror responses in a different format to the client's expected format. To dothis, set the ``_force_fallback_format`` attribute to the name of the format::class MyView(ContentNegotiatedView):_force_fallback_format = 'html'If a client doesn't provide an Accept header, then you can specify a defaultformat with ``_default_format``::class MyView(ContentNegotiatedView):_default_format = 'html'Built-in renderer views~~~~~~~~~~~~~~~~~~~~~~~``django_conneg`` includes the following built-in renderers in the``django_conneg.views`` module:* ``HTMLView`` (renders a ``.html`` template with media type ``text/html``)* ``TextView`` (renders a ``.txt`` template with media type ``text/plain``)* ``JSONView`` (coerces the context to JavaScript primitives and returns as ``application/json``)* ``JSONPView`` (as ``JSONView``, but wraps in a callback and returns as ``application/javascript``)Using these, you could define a view that renders to both HTML and JSON like this::from django_conneg.views import HTMLViewclass IndexView(JSONView, HTMLView):def get(self, request):# ...return self.render(request, context, 'index')Accessing renderer details--------------------------The renderer used to construct a response is exposed as a ``renderer``attribute on the response object::class IndexView(JSONView, HTMLView):def get(self, request):# ...response = self.render(request, context, 'index')response['X-Renderer-Format'] = response.renderer.formatreturn responseRenderer priorities-------------------Some user-agents might specify various media types with equal levels ofdesirability. For example, previous versions of Safari and Chrome `used to send<http://www.gethifi.com/blog/browser-rest-http-accept-headers#highlighter_222123>`_an ``Accept`` header like this::application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Without any additional hints it would be non-deterministic as to whether XML orXHTML is served.By passing a ``priority`` argument to the ``@renderer`` decorator you canspecify an ordering of renderers for such ambiguous situations::class IndexView(ContentNegotiatedView):@renderer(format='xml', mimetypes=('application/xml',), name='XML', priority=0)def render_xml(request, context, template_name):# ...@renderer(format='html', mimetypes=('application/xhtml+xml','text/html), name='HTML', priority=1)def render_html(request, context, template_name):# ...As higher-numbered priorities are preferred, this will result in HTML alwaysbeing prefered over XML in ambiguous situations.By default, ``django-conneg``'s built-in renderers have a priority of 0, exceptfor ``HTMLView`` and ``TextView``, which each have a priority of 1 for thereason given above.Improved 40x response handling------------------------------Django provides a couple of useful exceptions, ``Http404`` and``PermissionDenied``, which you may want to use in your application. However,it's only possible to customise the 404 site-wide (either by providing a``404.html`` template, or by setting ``handler404`` in your urlconf), anduntil Django 1.4 comes out, PermissionDenied will always result in a veryspartan error page.``django-conneg`` provides an ``ErrorCatchingView`` which you can use as amixin to customise the rendering of responses for these error situations::from django_conneg.views import HTMLView, ErrorCatchingViewclass IndexView(HTMLView, ErrorCatchingView):# ...You can then customise error responses in one of the following ways:* overriding the ``conneg/(forbidden|not_found|not_acceptable).(html|txt) templates* overriding ``error_403``, ``error_404`` or ``error_406`` methods on the view* overriding the ``error_template_names`` attribute to specify a non-standard template name:In the latter case, you can do something like::import httplibfrom django.util.datastructures import MergeDictfrom django_conneg.views import HTMLView, ErrorCatchingViewclass IndexView(HTMLView, ErrorCatchingView):# Provide a view-specific 404 page. Use MergeDict to use django_conneg's# defaults for other types of errors.error_template_names = MergeDict({httplib.NOT_FOUND: 'foo/404'},ErrorCatchingView.error_template_names)# ...Running the tests-----------------``django-conneg`` has a modest test suite. To run it, head to the root of therepository and run::django-admin test --settings=django_conneg.test_settings --pythonpath=.If you don't have Django, you'll need to install it as detailed in thePrerequisites section above.