From 5bb6301b7f53e3815ab1a81a5fa38721dc95b113 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Thu, 2 Feb 2012 18:19:44 +0200 Subject: Response as a subclass of HttpResponse - first draft, not quite there yet. --- djangorestframework/response.py | 141 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 12 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 96345cee..4f9b3a62 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -5,25 +5,62 @@ into a HTTP response depending on what renderers are set on your view and als depending on the accept header of the request. """ +from django.template.response import SimpleTemplateResponse from django.core.handlers.wsgi import STATUS_CODE_TEXT -__all__ = ('Response', 'ErrorResponse') +from djangorestframework.utils.mediatypes import order_by_precedence +from djangorestframework.utils import MSIE_USER_AGENT_REGEX +from djangorestframework import status + -# TODO: remove raw_content/cleaned_content and just use content? +__all__ = ('Response', 'ErrorResponse') -class Response(object): +class Response(SimpleTemplateResponse): """ An HttpResponse that may include content that hasn't yet been serialized. """ - def __init__(self, status=200, content=None, headers=None): - self.status = status - self.media_type = None + _ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params + _IGNORE_IE_ACCEPT_HEADER = True + + def __init__(self, content=None, status=None, request=None, renderers=None): + """ + content is the raw content. + + The set of renderers that the response can handle. + + Should be a tuple/list of classes as described in the :mod:`renderers` module. + """ + # First argument taken by `SimpleTemplateResponse.__init__` is template_name, + # which we don't need + super(Response, self).__init__(None, status=status) + # We need to store our content in raw content to avoid overriding HttpResponse's + # `content` property + self.raw_content = content self.has_content_body = content is not None - self.raw_content = content # content prior to filtering - self.cleaned_content = content # content after filtering - self.headers = headers or {} + self.request = request + if renderers is not None: + self.renderers = renderers + # TODO: must go + self.view = None + + # TODO: wrap this behavior around dispatch(), ensuring it works + # out of the box with existing Django classes that use render_to_response. + @property + def rendered_content(self): + """ + """ + renderer, media_type = self._determine_renderer() + # TODO: renderer *could* override media_type in .render() if required. + + # Set the media type of the response + self['Content-Type'] = renderer.media_type + + # Render the response content + if self.has_content_body: + return renderer.render(self.raw_content, media_type) + return renderer.render() @property def status_text(self): @@ -33,12 +70,92 @@ class Response(object): """ return STATUS_CODE_TEXT.get(self.status, '') + def _determine_accept_list(self): + request = self.request + if request is None: + return ['*/*'] + + if self._ACCEPT_QUERY_PARAM and request.GET.get(self._ACCEPT_QUERY_PARAM, None): + # Use _accept parameter override + return [request.GET.get(self._ACCEPT_QUERY_PARAM)] + elif (self._IGNORE_IE_ACCEPT_HEADER and + 'HTTP_USER_AGENT' in request.META and + MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT'])): + # Ignore MSIE's broken accept behavior and do something sensible instead + return ['text/html', '*/*'] + elif 'HTTP_ACCEPT' in request.META: + # Use standard HTTP Accept negotiation + return [token.strip() for token in request.META['HTTP_ACCEPT'].split(',')] + else: + # No accept header specified + return ['*/*'] + + def _determine_renderer(self): + """ + Determines the appropriate renderer for the output, given the client's 'Accept' header, + and the :attr:`renderers` set on this class. + + Returns a 2-tuple of `(renderer, media_type)` + + See: RFC 2616, Section 14 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + """ + # Check the acceptable media types against each renderer, + # attempting more specific media types first + # NB. The inner loop here isn't as bad as it first looks :) + # Worst case is we're looping over len(accept_list) * len(self.renderers) + renderers = [renderer_cls(self.view) for renderer_cls in self.renderers] + + for media_type_list in order_by_precedence(self._determine_accept_list()): + for renderer in renderers: + for media_type in media_type_list: + if renderer.can_handle_response(media_type): + return renderer, media_type + + # No acceptable renderers were found + raise ErrorResponse(content={'detail': 'Could not satisfy the client\'s Accept header', + 'available_types': self._rendered_media_types}, + status=status.HTTP_406_NOT_ACCEPTABLE, + renderers=self.renderers) + + def _get_renderers(self): + """ + This just provides a default when renderers havent' been set. + """ + if hasattr(self, '_renderers'): + return self._renderers + return () + + def _set_renderers(self, value): + self._renderers = value + + renderers = property(_get_renderers, _set_renderers) + + @property + def _rendered_media_types(self): + """ + Return an list of all the media types that this response can render. + """ + return [renderer.media_type for renderer in self.renderers] + + @property + def _rendered_formats(self): + """ + Return a list of all the formats that this response can render. + """ + return [renderer.format for renderer in self.renderers] + + @property + def _default_renderer(self): + """ + Return the response's default renderer class. + """ + return self.renderers[0] + -class ErrorResponse(BaseException): +class ErrorResponse(Response, BaseException): """ An exception representing an Response that should be returned immediately. Any content should be serialized as-is, without being filtered. """ + pass - def __init__(self, status, content=None, headers={}): - self.response = Response(status, content=content, headers=headers) -- cgit v1.2.3 From ca96b4523b4c09489e4bfe726a894a5c6ada78aa Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Tue, 7 Feb 2012 13:15:30 +0200 Subject: cleaned a bit Response/ResponseMixin code, added some documentation + renamed ErrorResponse to ImmediateResponse --- djangorestframework/response.py | 59 +++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 23 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 4f9b3a62..3b692b24 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -1,8 +1,18 @@ """ -The :mod:`response` module provides Response classes you can use in your -views to return a certain HTTP response. Typically a response is *rendered* -into a HTTP response depending on what renderers are set on your view and -als depending on the accept header of the request. +The :mod:`response` module provides :class:`Response` and :class:`ImmediateResponse` classes. + +`Response` is a subclass of `HttpResponse`, and can be similarly instantiated and returned +from any view. It is a bit smarter than Django's `HttpResponse` though, for it knows how +to use :mod:`renderers` to automatically render its content to a serial format. +This is achieved by : + + - determining the accepted types by checking for an overload or an `Accept` header in the request + - looking for a suitable renderer and using it on the content given at instantiation + + +`ImmediateResponse` is an exception that inherits from `Response`. It can be used +to abort the request handling (i.e. ``View.get``, ``View.put``, ...), +and immediately returning a response. """ from django.template.response import SimpleTemplateResponse @@ -13,7 +23,7 @@ from djangorestframework.utils import MSIE_USER_AGENT_REGEX from djangorestframework import status -__all__ = ('Response', 'ErrorResponse') +__all__ = ('Response', 'ImmediateResponse') class Response(SimpleTemplateResponse): @@ -26,15 +36,16 @@ class Response(SimpleTemplateResponse): def __init__(self, content=None, status=None, request=None, renderers=None): """ - content is the raw content. - - The set of renderers that the response can handle. + `content` is the raw content, not yet serialized. This must be simple Python + data that renderers can handle (cf: dict, str, ...) - Should be a tuple/list of classes as described in the :mod:`renderers` module. + `renderers` is a list/tuple of renderer instances and represents the set of renderers + that the response can handle. """ # First argument taken by `SimpleTemplateResponse.__init__` is template_name, # which we don't need super(Response, self).__init__(None, status=status) + # We need to store our content in raw content to avoid overriding HttpResponse's # `content` property self.raw_content = content @@ -42,17 +53,14 @@ class Response(SimpleTemplateResponse): self.request = request if renderers is not None: self.renderers = renderers - # TODO: must go - self.view = None - # TODO: wrap this behavior around dispatch(), ensuring it works - # out of the box with existing Django classes that use render_to_response. @property def rendered_content(self): """ + The final rendered content. Accessing this attribute triggers the complete rendering cycle : + selecting suitable renderer, setting response's actual content type, rendering data. """ renderer, media_type = self._determine_renderer() - # TODO: renderer *could* override media_type in .render() if required. # Set the media type of the response self['Content-Type'] = renderer.media_type @@ -65,12 +73,20 @@ class Response(SimpleTemplateResponse): @property def status_text(self): """ - Return reason text corresponding to our HTTP response status code. + Returns reason text corresponding to our HTTP response status code. Provided for convenience. """ return STATUS_CODE_TEXT.get(self.status, '') def _determine_accept_list(self): + """ + Returns a list of accepted media types. This list is determined from : + + 1. overload with `_ACCEPT_QUERY_PARAM` + 2. `Accept` header of the request + + If those are useless, a default value is returned instead. + """ request = self.request if request is None: return ['*/*'] @@ -92,7 +108,7 @@ class Response(SimpleTemplateResponse): def _determine_renderer(self): """ - Determines the appropriate renderer for the output, given the client's 'Accept' header, + Determines the appropriate renderer for the output, given the list of accepted media types, and the :attr:`renderers` set on this class. Returns a 2-tuple of `(renderer, media_type)` @@ -103,16 +119,14 @@ class Response(SimpleTemplateResponse): # attempting more specific media types first # NB. The inner loop here isn't as bad as it first looks :) # Worst case is we're looping over len(accept_list) * len(self.renderers) - renderers = [renderer_cls(self.view) for renderer_cls in self.renderers] - for media_type_list in order_by_precedence(self._determine_accept_list()): - for renderer in renderers: + for renderer in self.renderers: for media_type in media_type_list: if renderer.can_handle_response(media_type): return renderer, media_type # No acceptable renderers were found - raise ErrorResponse(content={'detail': 'Could not satisfy the client\'s Accept header', + raise ImmediateResponse(content={'detail': 'Could not satisfy the client\'s Accept header', 'available_types': self._rendered_media_types}, status=status.HTTP_406_NOT_ACCEPTABLE, renderers=self.renderers) @@ -152,10 +166,9 @@ class Response(SimpleTemplateResponse): return self.renderers[0] -class ErrorResponse(Response, BaseException): +class ImmediateResponse(Response, BaseException): """ - An exception representing an Response that should be returned immediately. - Any content should be serialized as-is, without being filtered. + A subclass of :class:`Response` used to abort the current request handling. """ pass -- cgit v1.2.3 From 6963fd3623ee217fe489abb25f0ffa8c0781e4cd Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Tue, 7 Feb 2012 16:22:14 +0200 Subject: some docs for Request/Response/mixins --- djangorestframework/response.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 3b692b24..29fffed3 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -2,12 +2,13 @@ The :mod:`response` module provides :class:`Response` and :class:`ImmediateResponse` classes. `Response` is a subclass of `HttpResponse`, and can be similarly instantiated and returned -from any view. It is a bit smarter than Django's `HttpResponse` though, for it knows how -to use :mod:`renderers` to automatically render its content to a serial format. -This is achieved by : +from any view. It is a bit smarter than Django's `HttpResponse`, for it renders automatically +its content to a serial format by using a list of :mod:`renderers`. - - determining the accepted types by checking for an overload or an `Accept` header in the request - - looking for a suitable renderer and using it on the content given at instantiation +To determine the content type to which it must render, default behaviour is to use standard +HTTP Accept header content negotiation. But `Response` also supports overriding the content type +by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers +from Internet Explorer user agents and use a sensible browser `Accept` header instead. `ImmediateResponse` is an exception that inherits from `Response`. It can be used @@ -29,19 +30,17 @@ __all__ = ('Response', 'ImmediateResponse') class Response(SimpleTemplateResponse): """ An HttpResponse that may include content that hasn't yet been serialized. + + Kwargs: + - content(object). The raw content, not yet serialized. This must be simple Python \ + data that renderers can handle (e.g.: `dict`, `str`, ...) + - renderers(list/tuple). The renderers to use for rendering the response content. """ _ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params _IGNORE_IE_ACCEPT_HEADER = True def __init__(self, content=None, status=None, request=None, renderers=None): - """ - `content` is the raw content, not yet serialized. This must be simple Python - data that renderers can handle (cf: dict, str, ...) - - `renderers` is a list/tuple of renderer instances and represents the set of renderers - that the response can handle. - """ # First argument taken by `SimpleTemplateResponse.__init__` is template_name, # which we don't need super(Response, self).__init__(None, status=status) @@ -132,9 +131,6 @@ class Response(SimpleTemplateResponse): renderers=self.renderers) def _get_renderers(self): - """ - This just provides a default when renderers havent' been set. - """ if hasattr(self, '_renderers'): return self._renderers return () -- cgit v1.2.3 From db0b01037a95946938ccd44eae14d8779bfff1a9 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Fri, 10 Feb 2012 10:18:39 +0200 Subject: made suggested fixes --- djangorestframework/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 29fffed3..c5fdccbc 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -125,7 +125,7 @@ class Response(SimpleTemplateResponse): return renderer, media_type # No acceptable renderers were found - raise ImmediateResponse(content={'detail': 'Could not satisfy the client\'s Accept header', + raise ImmediateResponse({'detail': 'Could not satisfy the client\'s Accept header', 'available_types': self._rendered_media_types}, status=status.HTTP_406_NOT_ACCEPTABLE, renderers=self.renderers) -- cgit v1.2.3 From 821844bb11e5262fb0dfc2fecf2add8fe18d3210 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Tue, 14 Feb 2012 10:05:28 +0200 Subject: fixed examples, corrected small bugs in the process --- djangorestframework/response.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index c5fdccbc..6c42c898 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -75,7 +75,7 @@ class Response(SimpleTemplateResponse): Returns reason text corresponding to our HTTP response status code. Provided for convenience. """ - return STATUS_CODE_TEXT.get(self.status, '') + return STATUS_CODE_TEXT.get(self.status_code, '') def _determine_accept_list(self): """ @@ -166,5 +166,11 @@ class ImmediateResponse(Response, BaseException): """ A subclass of :class:`Response` used to abort the current request handling. """ - pass + def __str__(self): + """ + Since this class is also an exception it has to provide a sensible + representation for the cases when it is treated as an exception. + """ + return ('%s must be caught in try/except block, ' + 'and returned as a normal HttpResponse' % self.__class__.__name__) -- cgit v1.2.3 From 21fcd3a90631e96e3fa210dd526abab9571ad6e1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 20 Feb 2012 09:36:03 +0000 Subject: Some cleanup --- djangorestframework/response.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index be2c3ebe..714cd5b8 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -6,13 +6,13 @@ from any view. It is a bit smarter than Django's `HttpResponse`, for it renders its content to a serial format by using a list of :mod:`renderers`. To determine the content type to which it must render, default behaviour is to use standard -HTTP Accept header content negotiation. But `Response` also supports overriding the content type +HTTP Accept header content negotiation. But `Response` also supports overriding the content type by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers from Internet Explorer user agents and use a sensible browser `Accept` header instead. `ImmediateResponse` is an exception that inherits from `Response`. It can be used -to abort the request handling (i.e. ``View.get``, ``View.put``, ...), +to abort the request handling (i.e. ``View.get``, ``View.put``, ...), and immediately returning a response. """ @@ -31,8 +31,8 @@ class Response(SimpleTemplateResponse): """ An HttpResponse that may include content that hasn't yet been serialized. - Kwargs: - - content(object). The raw content, not yet serialized. This must be simple Python \ + Kwargs: + - content(object). The raw content, not yet serialized. This must be simple Python data that renderers can handle (e.g.: `dict`, `str`, ...) - renderers(list/tuple). The renderers to use for rendering the response content. """ @@ -47,7 +47,7 @@ class Response(SimpleTemplateResponse): # We need to store our content in raw content to avoid overriding HttpResponse's # `content` property - self.raw_content = content + self.raw_content = content self.has_content_body = content is not None self.request = request if renderers is not None: @@ -56,7 +56,7 @@ class Response(SimpleTemplateResponse): @property def rendered_content(self): """ - The final rendered content. Accessing this attribute triggers the complete rendering cycle : + The final rendered content. Accessing this attribute triggers the complete rendering cycle : selecting suitable renderer, setting response's actual content type, rendering data. """ renderer, media_type = self._determine_renderer() @@ -80,9 +80,9 @@ class Response(SimpleTemplateResponse): def _determine_accept_list(self): """ Returns a list of accepted media types. This list is determined from : - + 1. overload with `_ACCEPT_QUERY_PARAM` - 2. `Accept` header of the request + 2. `Accept` header of the request If those are useless, a default value is returned instead. """ -- cgit v1.2.3 From af9e4f69d732cc643d6ec7ae13d4a19ac0332d44 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 21 Feb 2012 20:12:14 +0000 Subject: Merging master into develop --- djangorestframework/response.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 714cd5b8..1c260ecb 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -40,7 +40,7 @@ class Response(SimpleTemplateResponse): _ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params _IGNORE_IE_ACCEPT_HEADER = True - def __init__(self, content=None, status=None, request=None, renderers=None): + def __init__(self, content=None, status=None, request=None, renderers=None, headers=None): # First argument taken by `SimpleTemplateResponse.__init__` is template_name, # which we don't need super(Response, self).__init__(None, status=status) @@ -50,6 +50,7 @@ class Response(SimpleTemplateResponse): self.raw_content = content self.has_content_body = content is not None self.request = request + self.headers = headers and headers[:] or [] if renderers is not None: self.renderers = renderers -- cgit v1.2.3 From 242327d339fe1193a45c64cb20a2ba4c56044c3b Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Thu, 23 Feb 2012 08:54:25 +0200 Subject: hack to fix ImmediateResponse rendering --- djangorestframework/response.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index be2c3ebe..a352531f 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -53,6 +53,14 @@ class Response(SimpleTemplateResponse): if renderers is not None: self.renderers = renderers + def render(self): + #TODO: see ImmediateResponse + try: + return super(Response, self).render() + except ImmediateResponse as response: + response.renderers = self.renderers + return response.render() + @property def rendered_content(self): """ @@ -166,6 +174,18 @@ class ImmediateResponse(Response, Exception): """ A subclass of :class:`Response` used to abort the current request handling. """ + #TODO: this is just a temporary fix, the whole rendering/support for ImmediateResponse, should be remade : see issue #163 + + def render(self): + try: + return super(Response, self).render() + except ImmediateResponse as exc: + renderer, media_type = self._determine_renderer() + self.renderers.remove(renderer) + if len(self.renderers) == 0: + raise RuntimeError('Caught an ImmediateResponse while '\ + 'trying to render an ImmediateResponse') + return self.render() def __str__(self): """ -- cgit v1.2.3 From 1cde31c86d9423e9b7a7409c2ef2ba7c0500e47f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 25 Feb 2012 18:45:17 +0000 Subject: Massive merge --- djangorestframework/response.py | 110 +++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 57 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 1c260ecb..bedeb6c5 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -27,6 +27,10 @@ from djangorestframework import status __all__ = ('Response', 'ImmediateResponse') +class NotAcceptable(Exception): + pass + + class Response(SimpleTemplateResponse): """ An HttpResponse that may include content that hasn't yet been serialized. @@ -40,25 +44,30 @@ class Response(SimpleTemplateResponse): _ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params _IGNORE_IE_ACCEPT_HEADER = True - def __init__(self, content=None, status=None, request=None, renderers=None, headers=None): + def __init__(self, content=None, status=None, headers=None, view=None, request=None, renderers=None): # First argument taken by `SimpleTemplateResponse.__init__` is template_name, # which we don't need super(Response, self).__init__(None, status=status) - # We need to store our content in raw content to avoid overriding HttpResponse's - # `content` property self.raw_content = content self.has_content_body = content is not None - self.request = request self.headers = headers and headers[:] or [] - if renderers is not None: - self.renderers = renderers + self.view = view + self.request = request + self.renderers = renderers + + def get_renderers(self): + """ + Instantiates and returns the list of renderers the response will use. + """ + return [renderer(self.view) for renderer in self.renderers] @property def rendered_content(self): """ - The final rendered content. Accessing this attribute triggers the complete rendering cycle : - selecting suitable renderer, setting response's actual content type, rendering data. + The final rendered content. Accessing this attribute triggers the + complete rendering cycle: selecting suitable renderer, setting + response's actual content type, rendering data. """ renderer, media_type = self._determine_renderer() @@ -70,6 +79,13 @@ class Response(SimpleTemplateResponse): return renderer.render(self.raw_content, media_type) return renderer.render() + def render(self): + try: + return super(Response, self).render() + except NotAcceptable: + response = self._get_406_response() + return response.render() + @property def status_text(self): """ @@ -88,8 +104,6 @@ class Response(SimpleTemplateResponse): If those are useless, a default value is returned instead. """ request = self.request - if request is None: - return ['*/*'] if self._ACCEPT_QUERY_PARAM and request.GET.get(self._ACCEPT_QUERY_PARAM, None): # Use _accept parameter override @@ -108,70 +122,52 @@ class Response(SimpleTemplateResponse): def _determine_renderer(self): """ - Determines the appropriate renderer for the output, given the list of accepted media types, - and the :attr:`renderers` set on this class. + Determines the appropriate renderer for the output, given the list of + accepted media types, and the :attr:`renderers` set on this class. Returns a 2-tuple of `(renderer, media_type)` - 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 """ + + renderers = self.get_renderers() + accepts = self._determine_accept_list() + + # Not acceptable response - Ignore accept header. + if self.status_code == 406: + return (renderers[0], renderers[0].media_type) + # Check the acceptable media types against each renderer, # attempting more specific media types first # NB. The inner loop here isn't as bad as it first looks :) # Worst case is we're looping over len(accept_list) * len(self.renderers) - for media_type_list in order_by_precedence(self._determine_accept_list()): - for renderer in self.renderers: + for media_type_list in order_by_precedence(accepts): + for renderer in renderers: for media_type in media_type_list: if renderer.can_handle_response(media_type): return renderer, media_type # No acceptable renderers were found - raise ImmediateResponse({'detail': 'Could not satisfy the client\'s Accept header', - 'available_types': self._rendered_media_types}, - status=status.HTTP_406_NOT_ACCEPTABLE, - renderers=self.renderers) - - def _get_renderers(self): - if hasattr(self, '_renderers'): - return self._renderers - return () - - def _set_renderers(self, value): - self._renderers = value + raise NotAcceptable - renderers = property(_get_renderers, _set_renderers) - - @property - def _rendered_media_types(self): - """ - Return an list of all the media types that this response can render. - """ - return [renderer.media_type for renderer in self.renderers] - - @property - def _rendered_formats(self): - """ - Return a list of all the formats that this response can render. - """ - return [renderer.format for renderer in self.renderers] - - @property - def _default_renderer(self): - """ - Return the response's default renderer class. - """ - return self.renderers[0] + def _get_406_response(self): + renderer = self.renderers[0] + return Response( + { + 'detail': 'Could not satisfy the client\'s Accept header', + 'available_types': [renderer.media_type + for renderer in self.renderers] + }, + status=status.HTTP_406_NOT_ACCEPTABLE, + view=self.view, request=self.request, renderers=[renderer]) class ImmediateResponse(Response, Exception): """ - A subclass of :class:`Response` used to abort the current request handling. + An exception representing an Response that should be returned immediately. + Any content should be serialized as-is, without being filtered. """ - def __str__(self): - """ - Since this class is also an exception it has to provide a sensible - representation for the cases when it is treated as an exception. - """ - return ('%s must be caught in try/except block, ' - 'and returned as a normal HttpResponse' % self.__class__.__name__) + def __init__(self, *args, **kwargs): + self.response = Response(*args, **kwargs) -- cgit v1.2.3 From 73cc77553ed5411f1959a51574b156a47ad5340d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 26 Aug 2012 23:06:52 +0100 Subject: Drop ImmediateResponse --- djangorestframework/response.py | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index ea9a938c..ac16e79a 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -161,25 +161,3 @@ class Response(SimpleTemplateResponse): }, status=status.HTTP_406_NOT_ACCEPTABLE, view=self.view, request=self.request, renderers=[renderer]) - - -class ImmediateResponse(Response, Exception): - """ - An exception representing an Response that should be returned immediately. - Any content should be serialized as-is, without being filtered. - """ - #TODO: this is just a temporary fix, the whole rendering/support for ImmediateResponse, should be remade : see issue #163 - - def render(self): - try: - return super(Response, self).render() - except ImmediateResponse: - renderer, media_type = self._determine_renderer() - self.renderers.remove(renderer) - if len(self.renderers) == 0: - raise RuntimeError('Caught an ImmediateResponse while '\ - 'trying to render an ImmediateResponse') - return self.render() - - def __init__(self, *args, **kwargs): - self.response = Response(*args, **kwargs) -- cgit v1.2.3 From 9ea12d14125a2a4ddc58e35ba420656f2fd29eb2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 26 Aug 2012 23:16:18 +0100 Subject: Tweak docstrings --- djangorestframework/response.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index ac16e79a..f8a512d3 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -24,9 +24,6 @@ from djangorestframework.utils import MSIE_USER_AGENT_REGEX from djangorestframework import status -__all__ = ('Response', 'ImmediateResponse') - - class NotAcceptable(Exception): pass @@ -36,8 +33,9 @@ class Response(SimpleTemplateResponse): An HttpResponse that may include content that hasn't yet been serialized. Kwargs: - - content(object). The raw content, not yet serialized. This must be simple Python - data that renderers can handle (e.g.: `dict`, `str`, ...) + - content(object). The raw content, not yet serialized. + This must be native Python data that renderers can handle. + (e.g.: `dict`, `str`, ...) - renderers(list/tuple). The renderers to use for rendering the response content. """ -- cgit v1.2.3 From 0ef6b1c122b0f5c3cc195b44a06671aefa75c9f7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 2 Sep 2012 00:19:54 +0100 Subject: Remove mention of ImmediateResponse --- djangorestframework/response.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index f8a512d3..9dde1f01 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -9,11 +9,6 @@ To determine the content type to which it must render, default behaviour is to u HTTP Accept header content negotiation. But `Response` also supports overriding the content type by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers from Internet Explorer user agents and use a sensible browser `Accept` header instead. - - -`ImmediateResponse` is an exception that inherits from `Response`. It can be used -to abort the request handling (i.e. ``View.get``, ``View.put``, ...), -and immediately returning a response. """ from django.template.response import SimpleTemplateResponse -- cgit v1.2.3 From 149b00a070fcbfd44feee5b37096081e18356f93 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 3 Sep 2012 15:57:43 +0100 Subject: Added the api_view decorator --- djangorestframework/response.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 9dde1f01..4664e079 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -14,6 +14,7 @@ from Internet Explorer user agents and use a sensible browser `Accept` header in from django.template.response import SimpleTemplateResponse from django.core.handlers.wsgi import STATUS_CODE_TEXT +from djangorestframework.settings import api_settings from djangorestframework.utils.mediatypes import order_by_precedence from djangorestframework.utils import MSIE_USER_AGENT_REGEX from djangorestframework import status @@ -53,7 +54,12 @@ class Response(SimpleTemplateResponse): """ Instantiates and returns the list of renderers the response will use. """ - return [renderer(self.view) for renderer in self.renderers] + if self.renderers is None: + renderer_classes = api_settings.DEFAULT_RENDERERS + else: + renderer_classes = self.renderers + + return [cls(self.view) for cls in renderer_classes] @property def rendered_content(self): -- cgit v1.2.3 From 6af75d3a69c486b19adc6e2da00719094778eb31 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 4 Sep 2012 09:29:59 +0100 Subject: Remove some bits from utils --- djangorestframework/response.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 4664e079..65173200 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -11,15 +11,18 @@ by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore from Internet Explorer user agents and use a sensible browser `Accept` header instead. """ + +import re from django.template.response import SimpleTemplateResponse from django.core.handlers.wsgi import STATUS_CODE_TEXT - from djangorestframework.settings import api_settings from djangorestframework.utils.mediatypes import order_by_precedence -from djangorestframework.utils import MSIE_USER_AGENT_REGEX from djangorestframework import status +MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )') + + class NotAcceptable(Exception): pass -- cgit v1.2.3 From 1c78bf53dbc4f75cfdc240c72f4db9d2376cb9cb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 6 Sep 2012 13:49:15 +0100 Subject: Refactoring some basics --- djangorestframework/response.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 65173200..f8b3504e 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -144,9 +144,9 @@ class Response(SimpleTemplateResponse): # attempting more specific media types first # NB. The inner loop here isn't as bad as it first looks :) # Worst case is we're looping over len(accept_list) * len(self.renderers) - for media_type_list in order_by_precedence(accepts): + for media_type_set in order_by_precedence(accepts): for renderer in renderers: - for media_type in media_type_list: + for media_type in media_type_set: if renderer.can_handle_response(media_type): return renderer, media_type -- cgit v1.2.3 From 9dc7270cced6be898ff1c3a8855bf196bd89b912 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 6 Sep 2012 16:28:11 +0100 Subject: Move settings stuff actually into settings --- djangorestframework/response.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index f8b3504e..71b08a04 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -38,7 +38,7 @@ class Response(SimpleTemplateResponse): - renderers(list/tuple). The renderers to use for rendering the response content. """ - _ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params + _ACCEPT_QUERY_PARAM = api_settings.URL_ACCEPT_OVERRIDE _IGNORE_IE_ACCEPT_HEADER = True def __init__(self, content=None, status=None, headers=None, view=None, request=None, renderers=None): @@ -107,13 +107,16 @@ class Response(SimpleTemplateResponse): """ request = self.request - if self._ACCEPT_QUERY_PARAM and request.GET.get(self._ACCEPT_QUERY_PARAM, None): + if (self._ACCEPT_QUERY_PARAM and + request.GET.get(self._ACCEPT_QUERY_PARAM, None)): # Use _accept parameter override return [request.GET.get(self._ACCEPT_QUERY_PARAM)] elif (self._IGNORE_IE_ACCEPT_HEADER and 'HTTP_USER_AGENT' in request.META and - MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT'])): - # Ignore MSIE's broken accept behavior and do something sensible instead + MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and + request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'): + # Ignore MSIE's broken accept behavior except for AJAX requests + # and do something sensible instead return ['text/html', '*/*'] elif 'HTTP_ACCEPT' in request.META: # Use standard HTTP Accept negotiation -- cgit v1.2.3 From 1608583eaa5201340c554a767e8e48eecabe1f39 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 7 Sep 2012 11:12:24 +0100 Subject: Basic support for format suffixes --- djangorestframework/response.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 71b08a04..08e14199 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -41,7 +41,8 @@ class Response(SimpleTemplateResponse): _ACCEPT_QUERY_PARAM = api_settings.URL_ACCEPT_OVERRIDE _IGNORE_IE_ACCEPT_HEADER = True - def __init__(self, content=None, status=None, headers=None, view=None, request=None, renderers=None): + def __init__(self, content=None, status=None, headers=None, view=None, + request=None, renderers=None, format=None): # First argument taken by `SimpleTemplateResponse.__init__` is template_name, # which we don't need super(Response, self).__init__(None, status=status) @@ -52,6 +53,7 @@ class Response(SimpleTemplateResponse): self.view = view self.request = request self.renderers = renderers + self.format = format def get_renderers(self): """ @@ -62,6 +64,9 @@ class Response(SimpleTemplateResponse): else: renderer_classes = self.renderers + if self.format: + return [cls(self.view) for cls in renderer_classes + if cls.format == self.format] return [cls(self.view) for cls in renderer_classes] @property -- cgit v1.2.3 From 272c49685c8823068492ec9fadb7f982001244d7 Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Tue, 11 Sep 2012 14:17:26 +0100 Subject: Better naming for properties on views, requests and responses renderers is now renderer_classes parsers is now parser_classes authentication is now authentication_classes --- djangorestframework/response.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 08e14199..e1366bdb 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -35,14 +35,14 @@ class Response(SimpleTemplateResponse): - content(object). The raw content, not yet serialized. This must be native Python data that renderers can handle. (e.g.: `dict`, `str`, ...) - - renderers(list/tuple). The renderers to use for rendering the response content. + - renderer_classes(list/tuple). The renderers to use for rendering the response content. """ _ACCEPT_QUERY_PARAM = api_settings.URL_ACCEPT_OVERRIDE _IGNORE_IE_ACCEPT_HEADER = True def __init__(self, content=None, status=None, headers=None, view=None, - request=None, renderers=None, format=None): + request=None, renderer_classes=None, format=None): # First argument taken by `SimpleTemplateResponse.__init__` is template_name, # which we don't need super(Response, self).__init__(None, status=status) @@ -52,17 +52,17 @@ class Response(SimpleTemplateResponse): self.headers = headers and headers[:] or [] self.view = view self.request = request - self.renderers = renderers + self.renderer_classes = renderer_classes self.format = format def get_renderers(self): """ Instantiates and returns the list of renderers the response will use. """ - if self.renderers is None: + if self.renderer_classes is None: renderer_classes = api_settings.DEFAULT_RENDERERS else: - renderer_classes = self.renderers + renderer_classes = self.renderer_classes if self.format: return [cls(self.view) for cls in renderer_classes @@ -133,7 +133,7 @@ class Response(SimpleTemplateResponse): def _determine_renderer(self): """ Determines the appropriate renderer for the output, given the list of - accepted media types, and the :attr:`renderers` set on this class. + accepted media types, and the :attr:`renderer_classes` set on this class. Returns a 2-tuple of `(renderer, media_type)` @@ -162,12 +162,12 @@ class Response(SimpleTemplateResponse): raise NotAcceptable def _get_406_response(self): - renderer = self.renderers[0] + renderer = self.renderer_classes[0] return Response( { 'detail': 'Could not satisfy the client\'s Accept header', 'available_types': [renderer.media_type - for renderer in self.renderers] + for renderer in self.renderer_classes] }, status=status.HTTP_406_NOT_ACCEPTABLE, - view=self.view, request=self.request, renderers=[renderer]) + view=self.view, request=self.request, renderer_classes=[renderer]) -- cgit v1.2.3 From b3e29d9576f1b1b6d12f9abfeb4a06f669b45202 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 14 Sep 2012 22:42:29 +0100 Subject: Moved content negotiation out of response. Nicer exception handling now. --- djangorestframework/response.py | 168 ++++------------------------------------ 1 file changed, 17 insertions(+), 151 deletions(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index e1366bdb..205c0503 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -1,97 +1,34 @@ -""" -The :mod:`response` module provides :class:`Response` and :class:`ImmediateResponse` classes. - -`Response` is a subclass of `HttpResponse`, and can be similarly instantiated and returned -from any view. It is a bit smarter than Django's `HttpResponse`, for it renders automatically -its content to a serial format by using a list of :mod:`renderers`. - -To determine the content type to which it must render, default behaviour is to use standard -HTTP Accept header content negotiation. But `Response` also supports overriding the content type -by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers -from Internet Explorer user agents and use a sensible browser `Accept` header instead. -""" - - -import re from django.template.response import SimpleTemplateResponse from django.core.handlers.wsgi import STATUS_CODE_TEXT -from djangorestframework.settings import api_settings -from djangorestframework.utils.mediatypes import order_by_precedence -from djangorestframework import status - - -MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )') - - -class NotAcceptable(Exception): - pass class Response(SimpleTemplateResponse): """ - An HttpResponse that may include content that hasn't yet been serialized. - - Kwargs: - - content(object). The raw content, not yet serialized. - This must be native Python data that renderers can handle. - (e.g.: `dict`, `str`, ...) - - renderer_classes(list/tuple). The renderers to use for rendering the response content. + An HttpResponse that allows it's data to be rendered into + arbitrary media types. """ - _ACCEPT_QUERY_PARAM = api_settings.URL_ACCEPT_OVERRIDE - _IGNORE_IE_ACCEPT_HEADER = True + def __init__(self, data=None, status=None, headers=None, + renderer=None, media_type=None): + """ + Alters the init arguments slightly. + For example, drop 'template_name', and instead use 'data'. - def __init__(self, content=None, status=None, headers=None, view=None, - request=None, renderer_classes=None, format=None): - # First argument taken by `SimpleTemplateResponse.__init__` is template_name, - # which we don't need + Setting 'renderer' and 'media_type' will typically be defered, + For example being set automatically by the `APIView`. + """ super(Response, self).__init__(None, status=status) - - self.raw_content = content - self.has_content_body = content is not None + self.data = data self.headers = headers and headers[:] or [] - self.view = view - self.request = request - self.renderer_classes = renderer_classes - self.format = format - - def get_renderers(self): - """ - Instantiates and returns the list of renderers the response will use. - """ - if self.renderer_classes is None: - renderer_classes = api_settings.DEFAULT_RENDERERS - else: - renderer_classes = self.renderer_classes - - if self.format: - return [cls(self.view) for cls in renderer_classes - if cls.format == self.format] - return [cls(self.view) for cls in renderer_classes] + self.renderer = renderer + self.media_type = media_type @property def rendered_content(self): - """ - The final rendered content. Accessing this attribute triggers the - complete rendering cycle: selecting suitable renderer, setting - response's actual content type, rendering data. - """ - renderer, media_type = self._determine_renderer() - - # Set the media type of the response - self['Content-Type'] = renderer.media_type - - # Render the response content - if self.has_content_body: - return renderer.render(self.raw_content, media_type) - return renderer.render() - - def render(self): - try: - return super(Response, self).render() - except NotAcceptable: - response = self._get_406_response() - return response.render() + self['Content-Type'] = self.media_type + if self.data is None: + return self.renderer.render() + return self.renderer.render(self.data, self.media_type) @property def status_text(self): @@ -100,74 +37,3 @@ class Response(SimpleTemplateResponse): Provided for convenience. """ return STATUS_CODE_TEXT.get(self.status_code, '') - - def _determine_accept_list(self): - """ - Returns a list of accepted media types. This list is determined from : - - 1. overload with `_ACCEPT_QUERY_PARAM` - 2. `Accept` header of the request - - If those are useless, a default value is returned instead. - """ - request = self.request - - if (self._ACCEPT_QUERY_PARAM and - request.GET.get(self._ACCEPT_QUERY_PARAM, None)): - # Use _accept parameter override - return [request.GET.get(self._ACCEPT_QUERY_PARAM)] - elif (self._IGNORE_IE_ACCEPT_HEADER and - 'HTTP_USER_AGENT' in request.META and - MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and - request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'): - # Ignore MSIE's broken accept behavior except for AJAX requests - # and do something sensible instead - return ['text/html', '*/*'] - elif 'HTTP_ACCEPT' in request.META: - # Use standard HTTP Accept negotiation - return [token.strip() for token in request.META['HTTP_ACCEPT'].split(',')] - else: - # No accept header specified - return ['*/*'] - - def _determine_renderer(self): - """ - Determines the appropriate renderer for the output, given the list of - accepted media types, and the :attr:`renderer_classes` set on this class. - - Returns a 2-tuple of `(renderer, media_type)` - - See: RFC 2616, Section 14 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - """ - - renderers = self.get_renderers() - accepts = self._determine_accept_list() - - # Not acceptable response - Ignore accept header. - if self.status_code == 406: - return (renderers[0], renderers[0].media_type) - - # Check the acceptable media types against each renderer, - # attempting more specific media types first - # NB. The inner loop here isn't as bad as it first looks :) - # Worst case is we're looping over len(accept_list) * len(self.renderers) - for media_type_set in order_by_precedence(accepts): - for renderer in renderers: - for media_type in media_type_set: - if renderer.can_handle_response(media_type): - return renderer, media_type - - # No acceptable renderers were found - raise NotAcceptable - - def _get_406_response(self): - renderer = self.renderer_classes[0] - return Response( - { - 'detail': 'Could not satisfy the client\'s Accept header', - 'available_types': [renderer.media_type - for renderer in self.renderer_classes] - }, - status=status.HTTP_406_NOT_ACCEPTABLE, - view=self.view, request=self.request, renderer_classes=[renderer]) -- cgit v1.2.3 From a96211d3d1ba246512af5e32c31726a666c467ac Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 16 Sep 2012 21:48:55 +0100 Subject: Simplify negotiation. Drop MSIE hacks. Etc. --- djangorestframework/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py index 205c0503..29034e25 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -25,7 +25,7 @@ class Response(SimpleTemplateResponse): @property def rendered_content(self): - self['Content-Type'] = self.media_type + self['Content-Type'] = self.renderer.media_type if self.data is None: return self.renderer.render() return self.renderer.render(self.data, self.media_type) -- cgit v1.2.3 From 87dae4d8549c02fa9a57adb3bb876d249dae1f79 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:19:43 +0100 Subject: Remove old 'djangorestframework directories --- djangorestframework/response.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 djangorestframework/response.py (limited to 'djangorestframework/response.py') diff --git a/djangorestframework/response.py b/djangorestframework/response.py deleted file mode 100644 index 29034e25..00000000 --- a/djangorestframework/response.py +++ /dev/null @@ -1,39 +0,0 @@ -from django.template.response import SimpleTemplateResponse -from django.core.handlers.wsgi import STATUS_CODE_TEXT - - -class Response(SimpleTemplateResponse): - """ - An HttpResponse that allows it's data to be rendered into - arbitrary media types. - """ - - def __init__(self, data=None, status=None, headers=None, - renderer=None, media_type=None): - """ - Alters the init arguments slightly. - For example, drop 'template_name', and instead use 'data'. - - Setting 'renderer' and 'media_type' will typically be defered, - For example being set automatically by the `APIView`. - """ - super(Response, self).__init__(None, status=status) - self.data = data - self.headers = headers and headers[:] or [] - self.renderer = renderer - self.media_type = media_type - - @property - def rendered_content(self): - self['Content-Type'] = self.renderer.media_type - if self.data is None: - return self.renderer.render() - return self.renderer.render(self.data, self.media_type) - - @property - def status_text(self): - """ - Returns reason text corresponding to our HTTP response status code. - Provided for convenience. - """ - return STATUS_CODE_TEXT.get(self.status_code, '') -- cgit v1.2.3