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

View File

@ -49,7 +49,7 @@ class ModelResource(Resource):
#def get_form(self, content=None): #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: # if self.form:
# return super(self.__class__, self).get_form(content) # return super(self.__class__, self).get_form(content)
# #
@ -121,8 +121,8 @@ class ModelResource(Resource):
elif inspect.isfunction(thing): elif inspect.isfunction(thing):
if not inspect.getargspec(thing)[0]: if not inspect.getargspec(thing)[0]:
ret = _any(thing()) ret = _any(thing())
elif hasattr(thing, '__emittable__'): elif hasattr(thing, '__rendertable__'):
f = thing.__emittable__ f = thing.__rendertable__
if inspect.ismethod(f) and len(inspect.getargspec(f)[0]) == 1: if inspect.ismethod(f) and len(inspect.getargspec(f)[0]) == 1:
ret = _any(f()) ret = _any(f())
else: else:

View File

@ -4,13 +4,13 @@ from django.views.decorators.csrf import csrf_exempt
from djangorestframework.compat import View from djangorestframework.compat import View
from djangorestframework.response import Response, ErrorResponse from djangorestframework.response import Response, ErrorResponse
from djangorestframework.mixins import RequestMixin, ResponseMixin, AuthMixin 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: Figure how out references and named urls need to work nicely
# TODO: POST on existing 404 URL, PUT on existing 404 URL # 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'] __all__ = ['Resource']
@ -21,12 +21,12 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace', 'patch'] http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace', 'patch']
# List of emitters the resource can serialize the response with, ordered by preference. # List of renderers the resource can serialize the response with, ordered by preference.
emitters = ( emitters.JSONEmitter, renderers = ( renderers.JSONRenderer,
emitters.DocumentingHTMLEmitter, renderers.DocumentingHTMLRenderer,
emitters.DocumentingXHTMLEmitter, renderers.DocumentingXHTMLRenderer,
emitters.DocumentingPlainTextEmitter, renderers.DocumentingPlainTextRenderer,
emitters.XMLEmitter ) renderers.XMLRenderer )
# List of parsers the resource can parse the request with. # List of parsers the resource can parse the request with.
parsers = ( parsers.JSONParser, parsers = ( parsers.JSONParser,
@ -48,7 +48,7 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
# Allow name and description for the Resource to be set explicitly, # Allow name and description for the Resource to be set explicitly,
# overiding the default classname/docstring behaviour. # 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 name = None
description = 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. 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 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 return data
@ -123,7 +123,7 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
response.headers['Allow'] = ', '.join(self.allowed_methods) response.headers['Allow'] = ', '.join(self.allowed_methods)
response.headers['Vary'] = 'Authenticate, Accept' response.headers['Vary'] = 'Authenticate, Accept'
return self.emit(response) return self.render(response)
except: except:
import traceback import traceback
traceback.print_exc() traceback.print_exc()

View File

@ -28,7 +28,7 @@ urlpatterns = patterns('',
class BreadcrumbTests(TestCase): 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' urls = 'djangorestframework.tests.breadcrumbs'

View File

@ -125,7 +125,7 @@ def xml2dict(input):
# Piston: # Piston:
class XMLEmitter(): class XMLRenderer():
def _to_xml(self, xml, data): def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)): if isinstance(data, (list, tuple)):
for item in data: for item in data:
@ -156,4 +156,4 @@ class XMLEmitter():
return stream.getvalue() return stream.getvalue()
def dict2xml(input): 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.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.mixins import ResponseMixin
from djangorestframework.emitters import DEFAULT_EMITTERS from djangorestframework.renderers import DEFAULT_RENDERERS
from djangorestframework.response import Response from djangorestframework.response import Response
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
@ -9,8 +9,8 @@ from django.core.urlresolvers import reverse
class ExampleView(ResponseMixin, View): class ExampleView(ResponseMixin, View):
"""An example view using Django 1.3's class based views. """An example view using Django 1.3's class based views.
Uses djangorestframework's EmitterMixin to provide support for multiple output formats.""" Uses djangorestframework's RendererMixin to provide support for multiple output formats."""
emitters = DEFAULT_EMITTERS renderers = DEFAULT_RENDERERS
def get(self, request): def get(self, request):
response = Response(200, {'description': 'Some example content', 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.resource import Resource
from djangorestframework.response import Response from djangorestframework.response import Response
from djangorestframework.emitters import BaseEmitter from djangorestframework.renderers import BaseRenderer
from djangorestframework import status from djangorestframework import status
from pygments.formatters import HtmlFormatter 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:]] [os.remove(path) for path in list_dir_sorted_by_ctime(dir)[max_files:]]
class HTMLEmitter(BaseEmitter): class HTMLRenderer(BaseRenderer):
"""Basic emitter which just returns the content without any further serialization.""" """Basic renderer which just returns the content without any further serialization."""
media_type = 'text/html' media_type = 'text/html'
@ -68,8 +68,8 @@ class PygmentsRoot(Resource):
class PygmentsInstance(Resource): class PygmentsInstance(Resource):
"""Simply return the stored highlighted HTML file with the correct mime type. """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.""" This Resource only emits HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class."""
emitters = (HTMLEmitter,) renderers = (HTMLRenderer,)
def get(self, request, unique_id): def get(self, request, unique_id):
"""Return the highlighted snippet.""" """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... 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/ # (Use default renderer)
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/ -H 'Accept: text/plain' # (Use plaintext documentation renderer)
The examples provided: The examples provided:
1. A basic example using the [Resource](http://django-rest-framework.org/library/resource.html) class. 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. 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. 4. A generic object store API.
5. A code highlighting API. 5. A code highlighting API.
6. A blog posts and comments API. 6. A blog posts and comments API.