mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 01:47:59 +03:00 
			
		
		
		
	renderer API work
This commit is contained in:
		
							parent
							
								
									8f58ee489d
								
							
						
					
					
						commit
						527e4ffdf7
					
				| 
						 | 
					@ -17,14 +17,16 @@ import re
 | 
				
			||||||
from StringIO import StringIO
 | 
					from StringIO import StringIO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ('RequestMixin',
 | 
					__all__ = (
 | 
				
			||||||
           'ResponseMixin',
 | 
					    'RequestMixin',
 | 
				
			||||||
           'AuthMixin',
 | 
					    'ResponseMixin',
 | 
				
			||||||
           'ReadModelMixin',
 | 
					    'AuthMixin',
 | 
				
			||||||
           'CreateModelMixin',
 | 
					    'ReadModelMixin',
 | 
				
			||||||
           'UpdateModelMixin',
 | 
					    'CreateModelMixin',
 | 
				
			||||||
           'DeleteModelMixin',
 | 
					    'UpdateModelMixin',
 | 
				
			||||||
           'ListModelMixin')
 | 
					    'DeleteModelMixin',
 | 
				
			||||||
 | 
					    'ListModelMixin'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
########## Request Mixin ##########
 | 
					########## Request Mixin ##########
 | 
				
			||||||
| 
						 | 
					@ -267,7 +269,7 @@ class ResponseMixin(object):
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # Serialize the response content
 | 
					        # Serialize the response content
 | 
				
			||||||
        if response.has_content_body:
 | 
					        if response.has_content_body:
 | 
				
			||||||
            content = renderer(self).render(output=response.cleaned_content)
 | 
					            content = renderer(self).render(response.cleaned_content, renderer.media_type)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            content = renderer(self).render()
 | 
					            content = renderer(self).render()
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
"""Renderers are used to serialize a View's output into specific media types.
 | 
					"""
 | 
				
			||||||
 | 
					Renderers are used to serialize a View's output into specific media types.
 | 
				
			||||||
django-rest-framework also provides HTML and PlainText renderers that help self-document the API,
 | 
					django-rest-framework also provides HTML and PlainText renderers that help self-document the API,
 | 
				
			||||||
by serializing the output along with documentation regarding the Resource, output status and headers,
 | 
					by serializing the output along with documentation regarding the Resource, output status and headers,
 | 
				
			||||||
and providing forms and links depending on the allowed methods, renderers and parsers on the Resource. 
 | 
					and providing forms and links depending on the allowed methods, renderers and parsers on the Resource. 
 | 
				
			||||||
| 
						 | 
					@ -7,64 +8,78 @@ from django import forms
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.template import RequestContext, loader
 | 
					from django.template import RequestContext, loader
 | 
				
			||||||
from django.utils import simplejson as json
 | 
					from django.utils import simplejson as json
 | 
				
			||||||
from django import forms
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from djangorestframework.utils import dict2xml, url_resolves
 | 
					from djangorestframework import status
 | 
				
			||||||
from djangorestframework.compat import apply_markdown
 | 
					from djangorestframework.compat import apply_markdown
 | 
				
			||||||
 | 
					from djangorestframework.utils import dict2xml, url_resolves
 | 
				
			||||||
from djangorestframework.utils.breadcrumbs import get_breadcrumbs
 | 
					from djangorestframework.utils.breadcrumbs import get_breadcrumbs
 | 
				
			||||||
from djangorestframework.utils.description import get_name, get_description
 | 
					from djangorestframework.utils.description import get_name, get_description
 | 
				
			||||||
from djangorestframework import status
 | 
					from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from urllib import quote_plus
 | 
					 | 
				
			||||||
import string
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
from decimal import Decimal
 | 
					from decimal import Decimal
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import string
 | 
				
			||||||
 | 
					from urllib import quote_plus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = (
 | 
				
			||||||
 | 
					    'BaseRenderer',
 | 
				
			||||||
 | 
					    'JSONRenderer',
 | 
				
			||||||
 | 
					    'DocumentingHTMLRenderer',
 | 
				
			||||||
 | 
					    'DocumentingXHTMLRenderer',
 | 
				
			||||||
 | 
					    'DocumentingPlainTextRenderer',
 | 
				
			||||||
 | 
					    'XMLRenderer'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO: Rename verbose to something more appropriate
 | 
					 | 
				
			||||||
# TODO: Maybe None could be handled more cleanly.  It'd be nice if it was handled by default,
 | 
					 | 
				
			||||||
#       and only have an renderer output anything if it explicitly provides support for that.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BaseRenderer(object):
 | 
					class BaseRenderer(object):
 | 
				
			||||||
    """All renderers must extend this class, set the media_type attribute, and
 | 
					    """
 | 
				
			||||||
    override the render() function."""
 | 
					    All renderers must extend this class, set the media_type attribute, and
 | 
				
			||||||
 | 
					    override the render() function.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = None
 | 
					    media_type = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, view):
 | 
					    def __init__(self, view):
 | 
				
			||||||
        self.view = view
 | 
					        self.view = view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output=None, verbose=False):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        """By default render simply returns the ouput as-is.
 | 
					        """
 | 
				
			||||||
        Override this method to provide for other behaviour."""
 | 
					        By default render simply returns the ouput as-is.
 | 
				
			||||||
        if output is None:
 | 
					        Override this method to provide for other behavior.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if obj is None:
 | 
				
			||||||
            return ''
 | 
					            return ''
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return output
 | 
					        return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TemplateRenderer(BaseRenderer):
 | 
					class TemplateRenderer(BaseRenderer):
 | 
				
			||||||
    """A Base class provided for convenience.
 | 
					    """
 | 
				
			||||||
 | 
					    A Base class provided for convenience.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Render the output simply by using the given template.
 | 
					    Render the object simply by using the given template.
 | 
				
			||||||
    To create a template renderer, subclass this, and set
 | 
					    To create a template renderer, subclass this, and set
 | 
				
			||||||
    the ``media_type`` and ``template`` attributes"""
 | 
					    the ``media_type`` and ``template`` attributes
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = None
 | 
					    media_type = None
 | 
				
			||||||
    template = None
 | 
					    template = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output=None, verbose=False):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        if output is None:
 | 
					        if obj is None:
 | 
				
			||||||
            return ''
 | 
					            return ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        context = RequestContext(self.request, output)
 | 
					        context = RequestContext(self.request, obj)
 | 
				
			||||||
        return self.template.render(context)
 | 
					        return self.template.render(context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DocumentingTemplateRenderer(BaseRenderer):
 | 
					class DocumentingTemplateRenderer(BaseRenderer):
 | 
				
			||||||
    """Base class for renderers used to self-document the API.
 | 
					    """
 | 
				
			||||||
    Implementing classes should extend this class and set the template attribute."""
 | 
					    Base class for renderers used to self-document the API.
 | 
				
			||||||
 | 
					    Implementing classes should extend this class and set the template attribute.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    template = None
 | 
					    template = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_content(self, resource, request, output):
 | 
					    def _get_content(self, resource, request, obj, media_type):
 | 
				
			||||||
        """Get the content as if it had been renderted by a non-documenting renderer.
 | 
					        """Get the content as if it had been rendered by a non-documenting renderer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (Typically this will be the content as it would have been if the Resource had been
 | 
					        (Typically this will be the content as it would have been if the Resource had been
 | 
				
			||||||
        requested with an 'Accept: */*' header, although with verbose style formatting if appropriate.)"""
 | 
					        requested with an 'Accept: */*' header, although with verbose style formatting if appropriate.)"""
 | 
				
			||||||
| 
						 | 
					@ -73,8 +88,9 @@ class DocumentingTemplateRenderer(BaseRenderer):
 | 
				
			||||||
        renderers = [renderer for renderer in resource.renderers if not isinstance(renderer, DocumentingTemplateRenderer)]
 | 
					        renderers = [renderer for renderer in resource.renderers if not isinstance(renderer, DocumentingTemplateRenderer)]
 | 
				
			||||||
        if not renderers:
 | 
					        if not renderers:
 | 
				
			||||||
            return '[No renderers were found]'
 | 
					            return '[No renderers were found]'
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        content = renderers[0](resource).render(output, verbose=True)
 | 
					        media_type = add_media_type_param(media_type, 'indent', '4')
 | 
				
			||||||
 | 
					        content = renderers[0](resource).render(obj, media_type)
 | 
				
			||||||
        if not all(char in string.printable for char in content):
 | 
					        if not all(char in string.printable for char in content):
 | 
				
			||||||
            return '[%d bytes of binary content]'
 | 
					            return '[%d bytes of binary content]'
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
| 
						 | 
					@ -149,8 +165,8 @@ class DocumentingTemplateRenderer(BaseRenderer):
 | 
				
			||||||
        return GenericContentForm(resource)
 | 
					        return GenericContentForm(resource)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output=None):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        content = self._get_content(self.view, self.view.request, output)
 | 
					        content = self._get_content(self.view, self.view.request, obj, media_type)
 | 
				
			||||||
        form_instance = self._get_form_instance(self.view)
 | 
					        form_instance = self._get_form_instance(self.view)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if url_resolves(settings.LOGIN_URL) and url_resolves(settings.LOGOUT_URL):
 | 
					        if url_resolves(settings.LOGIN_URL) and url_resolves(settings.LOGOUT_URL):
 | 
				
			||||||
| 
						 | 
					@ -194,46 +210,63 @@ class DocumentingTemplateRenderer(BaseRenderer):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class JSONRenderer(BaseRenderer):
 | 
					class JSONRenderer(BaseRenderer):
 | 
				
			||||||
    """Renderer which serializes to JSON"""
 | 
					    """
 | 
				
			||||||
 | 
					    Renderer which serializes to JSON
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = 'application/json'
 | 
					    media_type = 'application/json'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output=None, verbose=False):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        if output is None:
 | 
					        if obj is None:
 | 
				
			||||||
            return ''
 | 
					            return ''
 | 
				
			||||||
        if verbose:
 | 
					
 | 
				
			||||||
            return json.dumps(output, indent=4, sort_keys=True)
 | 
					        indent = get_media_type_params(media_type).get('indent', None)
 | 
				
			||||||
        return json.dumps(output)
 | 
					        if indent is not None:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                indent = int(indent)
 | 
				
			||||||
 | 
					            except ValueError:
 | 
				
			||||||
 | 
					                indent = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sort_keys = indent and True or False
 | 
				
			||||||
 | 
					        return json.dumps(obj, indent=indent, sort_keys=sort_keys)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class XMLRenderer(BaseRenderer):
 | 
					class XMLRenderer(BaseRenderer):
 | 
				
			||||||
    """Renderer which serializes to XML."""
 | 
					    """
 | 
				
			||||||
 | 
					    Renderer which serializes to XML.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = 'application/xml'
 | 
					    media_type = 'application/xml'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output=None, verbose=False):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        if output is None:
 | 
					        if obj is None:
 | 
				
			||||||
            return ''
 | 
					            return ''
 | 
				
			||||||
        return dict2xml(output)
 | 
					        return dict2xml(obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DocumentingHTMLRenderer(DocumentingTemplateRenderer):
 | 
					class DocumentingHTMLRenderer(DocumentingTemplateRenderer):
 | 
				
			||||||
    """Renderer which provides a browsable HTML interface for an API.
 | 
					    """
 | 
				
			||||||
    See the examples listed in the django-rest-framework documentation to see this in actions."""
 | 
					    Renderer which provides a browsable HTML interface for an API.
 | 
				
			||||||
 | 
					    See the examples at http://api.django-rest-framework.org to see this in action.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = 'text/html'
 | 
					    media_type = 'text/html'
 | 
				
			||||||
    template = 'renderer.html'
 | 
					    template = 'renderer.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DocumentingXHTMLRenderer(DocumentingTemplateRenderer):
 | 
					class DocumentingXHTMLRenderer(DocumentingTemplateRenderer):
 | 
				
			||||||
    """Identical to DocumentingHTMLRenderer, except with an xhtml media type.
 | 
					    """
 | 
				
			||||||
 | 
					    Identical to DocumentingHTMLRenderer, except with an xhtml media type.
 | 
				
			||||||
    We need this to be listed in preference to xml in order to return HTML to WebKit based browsers,
 | 
					    We need this to be listed in preference to xml in order to return HTML to WebKit based browsers,
 | 
				
			||||||
    given their Accept headers."""
 | 
					    given their Accept headers.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = 'application/xhtml+xml'
 | 
					    media_type = 'application/xhtml+xml'
 | 
				
			||||||
    template = 'renderer.html'
 | 
					    template = 'renderer.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DocumentingPlainTextRenderer(DocumentingTemplateRenderer):
 | 
					class DocumentingPlainTextRenderer(DocumentingTemplateRenderer):
 | 
				
			||||||
    """Renderer that serializes the output with the default renderer, but also provides plain-text
 | 
					    """
 | 
				
			||||||
    doumentation of the returned status and headers, and of the resource's name and description.
 | 
					    Renderer that serializes the object with the default renderer, but also provides plain-text
 | 
				
			||||||
    Useful for browsing an API with command line tools."""
 | 
					    documentation of the returned status and headers, and of the resource's name and description.
 | 
				
			||||||
 | 
					    Useful for browsing an API with command line tools.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    media_type = 'text/plain'
 | 
					    media_type = 'text/plain'
 | 
				
			||||||
    template = 'renderer.txt'
 | 
					    template = 'renderer.txt'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,10 @@ from django.conf.urls.defaults import patterns, url
 | 
				
			||||||
from django import http
 | 
					from django import http
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from djangorestframework.compat import View
 | 
					from djangorestframework.compat import View
 | 
				
			||||||
from djangorestframework.renderers import BaseRenderer
 | 
					from djangorestframework.renderers import BaseRenderer, JSONRenderer
 | 
				
			||||||
from djangorestframework.mixins import ResponseMixin
 | 
					from djangorestframework.mixins import ResponseMixin
 | 
				
			||||||
from djangorestframework.response import Response
 | 
					from djangorestframework.response import Response
 | 
				
			||||||
 | 
					from djangorestframework.utils.mediatypes import add_media_type_param
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DUMMYSTATUS = 200
 | 
					DUMMYSTATUS = 200
 | 
				
			||||||
DUMMYCONTENT = 'dummycontent'
 | 
					DUMMYCONTENT = 'dummycontent'
 | 
				
			||||||
| 
						 | 
					@ -20,14 +21,14 @@ class MockView(ResponseMixin, View):
 | 
				
			||||||
class RendererA(BaseRenderer):
 | 
					class RendererA(BaseRenderer):
 | 
				
			||||||
    media_type = 'mock/renderera'
 | 
					    media_type = 'mock/renderera'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output, verbose=False):
 | 
					    def render(self, obj=None, content_type=None):
 | 
				
			||||||
        return RENDERER_A_SERIALIZER(output)
 | 
					        return RENDERER_A_SERIALIZER(obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RendererB(BaseRenderer):
 | 
					class RendererB(BaseRenderer):
 | 
				
			||||||
    media_type = 'mock/rendererb'
 | 
					    media_type = 'mock/rendererb'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, output, verbose=False):
 | 
					    def render(self, obj=None, content_type=None):
 | 
				
			||||||
        return RENDERER_B_SERIALIZER(output)
 | 
					        return RENDERER_B_SERIALIZER(obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = patterns('',
 | 
					urlpatterns = patterns('',
 | 
				
			||||||
| 
						 | 
					@ -36,7 +37,9 @@ urlpatterns = patterns('',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RendererIntegrationTests(TestCase):
 | 
					class RendererIntegrationTests(TestCase):
 | 
				
			||||||
    """End-to-end testing of renderers using an RendererMixin on a generic view."""
 | 
					    """
 | 
				
			||||||
 | 
					    End-to-end testing of renderers using an RendererMixin on a generic view.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    urls = 'djangorestframework.tests.renderers'
 | 
					    urls = 'djangorestframework.tests.renderers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,4 +76,32 @@ class RendererIntegrationTests(TestCase):
 | 
				
			||||||
    def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
 | 
					    def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
 | 
				
			||||||
        """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
 | 
					        """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
 | 
				
			||||||
        resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
 | 
					        resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
 | 
				
			||||||
        self.assertEquals(resp.status_code, 406)
 | 
					        self.assertEquals(resp.status_code, 406)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_flat_repr = '{"foo": ["bar", "baz"]}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_indented_repr = """{
 | 
				
			||||||
 | 
					  "foo": [
 | 
				
			||||||
 | 
					    "bar", 
 | 
				
			||||||
 | 
					    "baz"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRendererTests(TestCase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests specific to the JSON Renderer
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def test_without_content_type_args(self):
 | 
				
			||||||
 | 
					        obj = {'foo':['bar','baz']}
 | 
				
			||||||
 | 
					        renderer = JSONRenderer(None)
 | 
				
			||||||
 | 
					        content = renderer.render(obj, 'application/json')
 | 
				
			||||||
 | 
					        self.assertEquals(content, _flat_repr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_with_content_type_args(self):
 | 
				
			||||||
 | 
					        obj = {'foo':['bar','baz']}
 | 
				
			||||||
 | 
					        renderer = JSONRenderer(None)
 | 
				
			||||||
 | 
					        content = renderer.render(obj, 'application/json; indent=2')
 | 
				
			||||||
 | 
					        self.assertEquals(content, _indented_repr)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,15 @@ import xml.etree.ElementTree as ET
 | 
				
			||||||
MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
 | 
					MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def as_tuple(obj):
 | 
					def as_tuple(obj):
 | 
				
			||||||
    """Given obj return a tuple"""
 | 
					    """
 | 
				
			||||||
 | 
					    Given an object which may be a list/tuple, another object, or None,
 | 
				
			||||||
 | 
					    return that object in list form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IE:
 | 
				
			||||||
 | 
					    If the object is already a list/tuple just return it.
 | 
				
			||||||
 | 
					    If the object is not None, return it in a list with a single element.
 | 
				
			||||||
 | 
					    If the object is None return an empty list.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    if obj is None:
 | 
					    if obj is None:
 | 
				
			||||||
        return ()
 | 
					        return ()
 | 
				
			||||||
    elif isinstance(obj, list):
 | 
					    elif isinstance(obj, list):
 | 
				
			||||||
| 
						 | 
					@ -27,7 +35,9 @@ def as_tuple(obj):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
def url_resolves(url):
 | 
					def url_resolves(url):
 | 
				
			||||||
    """Return True if the given URL is mapped to a view in the urlconf, False otherwise."""
 | 
					    """
 | 
				
			||||||
 | 
					    Return True if the given URL is mapped to a view in the urlconf, False otherwise.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        resolve(url)
 | 
					        resolve(url)
 | 
				
			||||||
    except:
 | 
					    except:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ def media_type_matches(lhs, rhs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Valid media type strings include:
 | 
					    Valid media type strings include:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'application/json indent=4'
 | 
					    'application/json; indent=4'
 | 
				
			||||||
    'application/json'
 | 
					    'application/json'
 | 
				
			||||||
    'text/*'
 | 
					    'text/*'
 | 
				
			||||||
    '*/*'
 | 
					    '*/*'
 | 
				
			||||||
| 
						 | 
					@ -33,10 +33,28 @@ def is_form_media_type(media_type):
 | 
				
			||||||
    media_type = _MediaType(media_type)
 | 
					    media_type = _MediaType(media_type)
 | 
				
			||||||
    return media_type.full_type == 'application/x-www-form-urlencoded' or \
 | 
					    return media_type.full_type == 'application/x-www-form-urlencoded' or \
 | 
				
			||||||
           media_type.full_type == 'multipart/form-data'
 | 
					           media_type.full_type == 'multipart/form-data'
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
               
 | 
					
 | 
				
			||||||
 | 
					def add_media_type_param(media_type, key, val):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Add a key, value parameter to a media type string, and return the new media type string.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    media_type = _MediaType(media_type)
 | 
				
			||||||
 | 
					    media_type.params[key] = val
 | 
				
			||||||
 | 
					    return str(media_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_media_type_params(media_type):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Return a dictionary of the parameters on the given media type.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    return _MediaType(media_type).params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _MediaType(object):
 | 
					class _MediaType(object):
 | 
				
			||||||
    def __init__(self, media_type_str):
 | 
					    def __init__(self, media_type_str):
 | 
				
			||||||
 | 
					        if media_type_str is None:
 | 
				
			||||||
 | 
					            media_type_str = ''
 | 
				
			||||||
        self.orig = media_type_str
 | 
					        self.orig = media_type_str
 | 
				
			||||||
        self.full_type, self.params = parse_header(media_type_str)
 | 
					        self.full_type, self.params = parse_header(media_type_str)
 | 
				
			||||||
        self.main_type, sep, self.sub_type = self.full_type.partition('/')
 | 
					        self.main_type, sep, self.sub_type = self.full_type.partition('/')
 | 
				
			||||||
| 
						 | 
					@ -94,5 +112,8 @@ class _MediaType(object):
 | 
				
			||||||
        return unicode(self).encode('utf-8')
 | 
					        return unicode(self).encode('utf-8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __unicode__(self):
 | 
					    def __unicode__(self):
 | 
				
			||||||
        return self.orig
 | 
					        ret = "%s/%s" % (self.main_type, self.sub_type)
 | 
				
			||||||
 | 
					        for key, val in self.params.items():
 | 
				
			||||||
 | 
					            ret += "; %s=%s" % (key, val)
 | 
				
			||||||
 | 
					        return ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,11 +7,13 @@ from djangorestframework.mixins import *
 | 
				
			||||||
from djangorestframework import resource, renderers, parsers, authentication, permissions, validators, status
 | 
					from djangorestframework import resource, renderers, parsers, authentication, permissions, validators, status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ('BaseView',
 | 
					__all__ = (
 | 
				
			||||||
           'ModelView',
 | 
					    'BaseView',
 | 
				
			||||||
           'InstanceModelView',
 | 
					    'ModelView',
 | 
				
			||||||
           'ListOrModelView',
 | 
					    'InstanceModelView',
 | 
				
			||||||
           'ListOrCreateModelView')
 | 
					    'ListOrModelView',
 | 
				
			||||||
 | 
					    'ListOrCreateModelView'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,55 +80,59 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
 | 
				
			||||||
    # all other authentication is CSRF exempt.
 | 
					    # all other authentication is CSRF exempt.
 | 
				
			||||||
    @csrf_exempt
 | 
					    @csrf_exempt
 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
        self.request = request
 | 
					 | 
				
			||||||
        self.args = args
 | 
					 | 
				
			||||||
        self.kwargs = kwargs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Calls to 'reverse' will not be fully qualified unless we set the scheme/host/port here.
 | 
					 | 
				
			||||||
        prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host())
 | 
					 | 
				
			||||||
        set_script_prefix(prefix)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            # If using a form POST with '_method'/'_content'/'_content_type' overrides, then alter
 | 
					            self.request = request
 | 
				
			||||||
            # self.method, self.content_type, self.RAW_CONTENT & self.CONTENT appropriately.
 | 
					            self.args = args
 | 
				
			||||||
            self.perform_form_overloading()
 | 
					            self.kwargs = kwargs
 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Authenticate and check request is has the relevant permissions
 | 
					 | 
				
			||||||
            self._check_permissions()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Get the appropriate handler method
 | 
					 | 
				
			||||||
            if self.method.lower() in self.http_method_names:
 | 
					 | 
				
			||||||
                handler = getattr(self, self.method.lower(), self.http_method_not_allowed)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                handler = self.http_method_not_allowed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            response_obj = handler(request, *args, **kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Allow return value to be either Response, or an object, or None
 | 
					 | 
				
			||||||
            if isinstance(response_obj, Response):
 | 
					 | 
				
			||||||
                response = response_obj
 | 
					 | 
				
			||||||
            elif response_obj is not None:
 | 
					 | 
				
			||||||
                response = Response(status.HTTP_200_OK, response_obj)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                response = Response(status.HTTP_204_NO_CONTENT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Pre-serialize filtering (eg filter complex objects into natively serializable types)
 | 
					 | 
				
			||||||
            response.cleaned_content = self.resource.object_to_serializable(response.raw_content)
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        except ErrorResponse, exc:
 | 
					            # Calls to 'reverse' will not be fully qualified unless we set the scheme/host/port here.
 | 
				
			||||||
            response = exc.response
 | 
					            prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host())
 | 
				
			||||||
 | 
					            set_script_prefix(prefix)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                # If using a form POST with '_method'/'_content'/'_content_type' overrides, then alter
 | 
				
			||||||
 | 
					                # self.method, self.content_type, self.RAW_CONTENT & self.CONTENT appropriately.
 | 
				
			||||||
 | 
					                self.perform_form_overloading()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                # Authenticate and check request is has the relevant permissions
 | 
				
			||||||
 | 
					                self._check_permissions()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                # Get the appropriate handler method
 | 
				
			||||||
 | 
					                if self.method.lower() in self.http_method_names:
 | 
				
			||||||
 | 
					                    handler = getattr(self, self.method.lower(), self.http_method_not_allowed)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    handler = self.http_method_not_allowed
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                response_obj = handler(request, *args, **kwargs)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                # Allow return value to be either Response, or an object, or None
 | 
				
			||||||
 | 
					                if isinstance(response_obj, Response):
 | 
				
			||||||
 | 
					                    response = response_obj
 | 
				
			||||||
 | 
					                elif response_obj is not None:
 | 
				
			||||||
 | 
					                    response = Response(status.HTTP_200_OK, response_obj)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    response = Response(status.HTTP_204_NO_CONTENT)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                # Pre-serialize filtering (eg filter complex objects into natively serializable types)
 | 
				
			||||||
 | 
					                response.cleaned_content = self.resource.object_to_serializable(response.raw_content)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            except ErrorResponse, exc:
 | 
				
			||||||
 | 
					                response = exc.response
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                import traceback
 | 
				
			||||||
 | 
					                traceback.print_exc()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            # Always add these headers.
 | 
				
			||||||
 | 
					            #
 | 
				
			||||||
 | 
					            # TODO - this isn't actually the correct way to set the vary header,
 | 
				
			||||||
 | 
					            # also it's currently sub-obtimal for HTTP caching - need to sort that out. 
 | 
				
			||||||
 | 
					            response.headers['Allow'] = ', '.join(self.allowed_methods)
 | 
				
			||||||
 | 
					            response.headers['Vary'] = 'Authenticate, Accept'
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            return self.render(response)
 | 
				
			||||||
        except:
 | 
					        except:
 | 
				
			||||||
            import traceback
 | 
					            import traceback
 | 
				
			||||||
            traceback.print_exc()
 | 
					            traceback.print_exc()
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Always add these headers.
 | 
					 | 
				
			||||||
        #
 | 
					 | 
				
			||||||
        # TODO - this isn't actually the correct way to set the vary header,
 | 
					 | 
				
			||||||
        # also it's currently sub-obtimal for HTTP caching - need to sort that out. 
 | 
					 | 
				
			||||||
        response.headers['Allow'] = ', '.join(self.allowed_methods)
 | 
					 | 
				
			||||||
        response.headers['Vary'] = 'Authenticate, Accept'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return self.render(response)
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user