emitters -> renderers

This commit is contained in:
Tom Christie 2011-04-28 19:54:30 +01:00
parent ad552107ff
commit 93aa065fa9
8 changed files with 57 additions and 57 deletions

View File

@ -233,8 +233,8 @@ class RequestMixin(object):
@property
def default_parser(self):
"""Return the view's most preffered emitter.
(This has no behavioural effect, but is may be used by documenting emitters)"""
"""Return the view's most preffered renderer.
(This has no behavioural effect, but is may be used by documenting renderers)"""
return self.parsers[0]
@ -249,7 +249,7 @@ class RequestMixin(object):
########## ResponseMixin ##########
class ResponseMixin(object):
"""Adds behaviour for pluggable Emitters to a :class:`.Resource` or Django :class:`View`. class.
"""Adds behaviour for pluggable Renderers to a :class:`.Resource` or Django :class:`View`. class.
Default behaviour is to use standard HTTP Accept header content negotiation.
Also supports overidding the content type by specifying an _accept= parameter in the URL.
@ -260,7 +260,7 @@ class ResponseMixin(object):
#request = None
#response = None
emitters = ()
renderers = ()
#def render_to_response(self, obj):
# if isinstance(obj, Response):
@ -285,21 +285,21 @@ class ResponseMixin(object):
# return content
def emit(self, response):
def render(self, response):
"""Takes a :class:`Response` object and returns a Django :class:`HttpResponse`."""
self.response = response
try:
emitter = self._determine_emitter(self.request)
renderer = self._determine_renderer(self.request)
except ErrorResponse, exc:
emitter = self.default_emitter
renderer = self.default_renderer
response = exc.response
# Serialize the response content
if response.has_content_body:
content = emitter(self).emit(output=response.cleaned_content)
content = renderer(self).render(output=response.cleaned_content)
else:
content = emitter(self).emit()
content = renderer(self).render()
# Munge DELETE Response code to allow us to return content
# (Do this *after* we've rendered the template so that we include the normal deletion response code in the output)
@ -307,16 +307,16 @@ class ResponseMixin(object):
response.status = 200
# Build the HTTP Response
# TODO: Check if emitter.mimetype is underspecified, or if a content-type header has been set
resp = HttpResponse(content, mimetype=emitter.media_type, status=response.status)
# TODO: Check if renderer.mimetype is underspecified, or if a content-type header has been set
resp = HttpResponse(content, mimetype=renderer.media_type, status=response.status)
for (key, val) in response.headers.items():
resp[key] = val
return resp
def _determine_emitter(self, request):
"""Return the appropriate emitter for the output, given the client's 'Accept' header,
def _determine_renderer(self, request):
"""Return the appropriate renderer for the output, given the client's 'Accept' header,
and the content types that this Resource knows how to serve.
See: RFC 2616, Section 14 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html"""
@ -333,7 +333,7 @@ class ResponseMixin(object):
accept_list = request.META["HTTP_ACCEPT"].split(',')
else:
# No accept header specified
return self.default_emitter
return self.default_renderer
# Parse the accept header into a dict of {qvalue: set of media types}
# We ignore mietype parameters
@ -363,34 +363,34 @@ class ResponseMixin(object):
for accept_set in accept_sets:
# Return any exact match
for emitter in self.emitters:
if emitter.media_type in accept_set:
return emitter
for renderer in self.renderers:
if renderer.media_type in accept_set:
return renderer
# Return any subtype match
for emitter in self.emitters:
if emitter.media_type.split('/')[0] + '/*' in accept_set:
return emitter
for renderer in self.renderers:
if renderer.media_type.split('/')[0] + '/*' in accept_set:
return renderer
# Return default
if '*/*' in accept_set:
return self.default_emitter
return self.default_renderer
raise ErrorResponse(status.HTTP_406_NOT_ACCEPTABLE,
{'detail': 'Could not satisfy the client\'s Accept header',
'available_types': self.emitted_media_types})
'available_types': self.renderted_media_types})
@property
def emitted_media_types(self):
"""Return an list of all the media types that this resource can emit."""
return [emitter.media_type for emitter in self.emitters]
def renderted_media_types(self):
"""Return an list of all the media types that this resource can render."""
return [renderer.media_type for renderer in self.renderers]
@property
def default_emitter(self):
"""Return the resource's most prefered emitter.
(This emitter is used if the client does not send and Accept: header, or sends Accept: */*)"""
return self.emitters[0]
def default_renderer(self):
"""Return the resource's most prefered renderer.
(This renderer is used if the client does not send and Accept: header, or sends Accept: */*)"""
return self.renderers[0]
########## Auth Mixin ##########

View File

@ -49,7 +49,7 @@ class ModelResource(Resource):
#def get_form(self, content=None):
# """Return a form that may be used in validation and/or rendering an html emitter"""
# """Return a form that may be used in validation and/or rendering an html renderer"""
# if self.form:
# return super(self.__class__, self).get_form(content)
#
@ -121,8 +121,8 @@ class ModelResource(Resource):
elif inspect.isfunction(thing):
if not inspect.getargspec(thing)[0]:
ret = _any(thing())
elif hasattr(thing, '__emittable__'):
f = thing.__emittable__
elif hasattr(thing, '__rendertable__'):
f = thing.__rendertable__
if inspect.ismethod(f) and len(inspect.getargspec(f)[0]) == 1:
ret = _any(f())
else:

View File

@ -4,13 +4,13 @@ from django.views.decorators.csrf import csrf_exempt
from djangorestframework.compat import View
from djangorestframework.response import Response, ErrorResponse
from djangorestframework.mixins import RequestMixin, ResponseMixin, AuthMixin
from djangorestframework import emitters, parsers, authenticators, permissions, validators, status
from djangorestframework import renderers, parsers, authenticators, permissions, validators, status
# TODO: Figure how out references and named urls need to work nicely
# TODO: POST on existing 404 URL, PUT on existing 404 URL
#
# NEXT: Exceptions on func() -> 500, tracebacks emitted if settings.DEBUG
# NEXT: Exceptions on func() -> 500, tracebacks renderted if settings.DEBUG
__all__ = ['Resource']
@ -21,12 +21,12 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace', 'patch']
# List of emitters the resource can serialize the response with, ordered by preference.
emitters = ( emitters.JSONEmitter,
emitters.DocumentingHTMLEmitter,
emitters.DocumentingXHTMLEmitter,
emitters.DocumentingPlainTextEmitter,
emitters.XMLEmitter )
# List of renderers the resource can serialize the response with, ordered by preference.
renderers = ( renderers.JSONRenderer,
renderers.DocumentingHTMLRenderer,
renderers.DocumentingXHTMLRenderer,
renderers.DocumentingPlainTextRenderer,
renderers.XMLRenderer )
# List of parsers the resource can parse the request with.
parsers = ( parsers.JSONParser,
@ -48,7 +48,7 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
# Allow name and description for the Resource to be set explicitly,
# overiding the default classname/docstring behaviour.
# These are used for documentation in the standard html and text emitters.
# These are used for documentation in the standard html and text renderers.
name = None
description = None
@ -69,7 +69,7 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
Eg filter complex objects that cannot be serialized by json/xml/etc into basic objects that can.
TODO: This is going to be removed. I think that the 'fields' behaviour is going to move into
the EmitterMixin and Emitter classes."""
the RendererMixin and Renderer classes."""
return data
@ -123,7 +123,7 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
response.headers['Allow'] = ', '.join(self.allowed_methods)
response.headers['Vary'] = 'Authenticate, Accept'
return self.emit(response)
return self.render(response)
except:
import traceback
traceback.print_exc()

View File

@ -28,7 +28,7 @@ urlpatterns = patterns('',
class BreadcrumbTests(TestCase):
"""Tests the breadcrumb functionality used by the HTML emitter."""
"""Tests the breadcrumb functionality used by the HTML renderer."""
urls = 'djangorestframework.tests.breadcrumbs'

View File

@ -125,7 +125,7 @@ def xml2dict(input):
# Piston:
class XMLEmitter():
class XMLRenderer():
def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)):
for item in data:
@ -156,4 +156,4 @@ class XMLEmitter():
return stream.getvalue()
def dict2xml(input):
return XMLEmitter().dict2xml(input)
return XMLRenderer().dict2xml(input)

View File

@ -1,6 +1,6 @@
from djangorestframework.compat import View # Use Django 1.3's django.views.generic.View, or fall back to a clone of that if Django < 1.3
from djangorestframework.mixins import ResponseMixin
from djangorestframework.emitters import DEFAULT_EMITTERS
from djangorestframework.renderers import DEFAULT_RENDERERS
from djangorestframework.response import Response
from django.conf.urls.defaults import patterns, url
@ -9,8 +9,8 @@ from django.core.urlresolvers import reverse
class ExampleView(ResponseMixin, View):
"""An example view using Django 1.3's class based views.
Uses djangorestframework's EmitterMixin to provide support for multiple output formats."""
emitters = DEFAULT_EMITTERS
Uses djangorestframework's RendererMixin to provide support for multiple output formats."""
renderers = DEFAULT_RENDERERS
def get(self, request):
response = Response(200, {'description': 'Some example content',

View File

@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse
from djangorestframework.resource import Resource
from djangorestframework.response import Response
from djangorestframework.emitters import BaseEmitter
from djangorestframework.renderers import BaseRenderer
from djangorestframework import status
from pygments.formatters import HtmlFormatter
@ -32,8 +32,8 @@ def remove_oldest_files(dir, max_files):
[os.remove(path) for path in list_dir_sorted_by_ctime(dir)[max_files:]]
class HTMLEmitter(BaseEmitter):
"""Basic emitter which just returns the content without any further serialization."""
class HTMLRenderer(BaseRenderer):
"""Basic renderer which just returns the content without any further serialization."""
media_type = 'text/html'
@ -68,8 +68,8 @@ class PygmentsRoot(Resource):
class PygmentsInstance(Resource):
"""Simply return the stored highlighted HTML file with the correct mime type.
This Resource only emits HTML and uses a standard HTML emitter rather than the emitters.DocumentingHTMLEmitter class."""
emitters = (HTMLEmitter,)
This Resource only emits HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class."""
renderers = (HTMLRenderer,)
def get(self, request, unique_id):
"""Return the highlighted snippet."""

View File

@ -11,14 +11,14 @@ class Sandbox(Resource):
All the example APIs allow anonymous access, and can be navigated either through the browser or from the command line...
bash: curl -X GET http://api.django-rest-framework.org/ # (Use default emitter)
bash: curl -X GET http://api.django-rest-framework.org/ -H 'Accept: text/plain' # (Use plaintext documentation emitter)
bash: curl -X GET http://api.django-rest-framework.org/ # (Use default renderer)
bash: curl -X GET http://api.django-rest-framework.org/ -H 'Accept: text/plain' # (Use plaintext documentation renderer)
The examples provided:
1. A basic example using the [Resource](http://django-rest-framework.org/library/resource.html) class.
2. A basic example using the [ModelResource](http://django-rest-framework.org/library/modelresource.html) class.
3. An basic example using Django 1.3's [class based views](http://docs.djangoproject.com/en/dev/topics/class-based-views/) and djangorestframework's [EmitterMixin](http://django-rest-framework.org/library/emitters.html).
3. An basic example using Django 1.3's [class based views](http://docs.djangoproject.com/en/dev/topics/class-based-views/) and djangorestframework's [RendererMixin](http://django-rest-framework.org/library/renderers.html).
4. A generic object store API.
5. A code highlighting API.
6. A blog posts and comments API.