diff options
| -rw-r--r-- | docs/api-guide/content-negotiation.md | 6 | ||||
| -rw-r--r-- | rest_framework/negotiation.py | 33 | ||||
| -rw-r--r-- | rest_framework/request.py | 2 | ||||
| -rw-r--r-- | rest_framework/tests/negotiation.py | 10 | ||||
| -rw-r--r-- | rest_framework/views.py | 8 | 
5 files changed, 30 insertions, 29 deletions
| diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index ad98de3b..b95091c5 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -7,3 +7,9 @@  > — [RFC 2616][cite], Fielding et al.  [cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html + +**TODO**: Describe content negotiation style used by REST framework. + +## Custom content negotiation + +It's unlikley that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed.  To implement a custom content negotiation scheme, override `BaseContentNegotiation`, and implement the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)`
\ No newline at end of file diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py index 8b22f669..444f8056 100644 --- a/rest_framework/negotiation.py +++ b/rest_framework/negotiation.py @@ -4,45 +4,34 @@ from rest_framework.utils.mediatypes import order_by_precedence, media_type_matc  class BaseContentNegotiation(object): -    def negotiate(self, request, renderers, format=None, force=False): -        raise NotImplementedError('.negotiate() must be implemented') +    def select_parser(self, request, parsers): +        raise NotImplementedError('.select_parser() must be implemented') +    def select_renderer(self, request, renderers, format_suffix=None): +        raise NotImplementedError('.select_renderer() must be implemented') -class DefaultContentNegotiation(object): + +class DefaultContentNegotiation(BaseContentNegotiation):      settings = api_settings -    def select_parser(self, parsers, media_type): +    def select_parser(self, request, parsers):          """          Given a list of parsers and a media type, return the appropriate          parser to handle the incoming request.          """          for parser in parsers: -            if media_type_matches(parser.media_type, media_type): +            if media_type_matches(parser.media_type, request.content_type):                  return parser          return None -    def negotiate(self, request, renderers, format=None, force=False): +    def select_renderer(self, request, renderers, format_suffix=None):          """          Given a request and a list of renderers, return a two-tuple of:          (renderer, media type). - -        If force is set, then suppress exceptions, and forcibly return a -        fallback renderer and media_type. -        """ -        try: -            return self.unforced_negotiate(request, renderers, format) -        except (exceptions.InvalidFormat, exceptions.NotAcceptable): -            if force: -                return (renderers[0], renderers[0].media_type) -            raise - -    def unforced_negotiate(self, request, renderers, format=None): -        """ -        As `.negotiate()`, but does not take the optional `force` agument, -        or suppress exceptions.          """          # Allow URL style format override.  eg. "?format=json -        format = format or request.GET.get(self.settings.URL_FORMAT_OVERRIDE) +        format_query_param = self.settings.URL_FORMAT_OVERRIDE +        format = format_suffix or request.GET.get(format_query_param)          if format:              renderers = self.filter_renderers(renderers, format) diff --git a/rest_framework/request.py b/rest_framework/request.py index b9d55de4..b212680f 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -266,7 +266,7 @@ class Request(object):          if stream is None or media_type is None:              return (None, None) -        parser = self.negotiator.select_parser(self.parsers, media_type) +        parser = self.negotiator.select_parser(self, self.parsers)          if not parser:              raise exceptions.UnsupportedMediaType(media_type) diff --git a/rest_framework/tests/negotiation.py b/rest_framework/tests/negotiation.py index d8265b43..e06354ea 100644 --- a/rest_framework/tests/negotiation.py +++ b/rest_framework/tests/negotiation.py @@ -18,20 +18,20 @@ class TestAcceptedMediaType(TestCase):          self.renderers = [MockJSONRenderer(), MockHTMLRenderer()]          self.negotiator = DefaultContentNegotiation() -    def negotiate(self, request): -        return self.negotiator.negotiate(request, self.renderers) +    def select_renderer(self, request): +        return self.negotiator.select_renderer(request, self.renderers)      def test_client_without_accept_use_renderer(self):          request = factory.get('/') -        accepted_renderer, accepted_media_type = self.negotiate(request) +        accepted_renderer, accepted_media_type = self.select_renderer(request)          self.assertEquals(accepted_media_type, 'application/json')      def test_client_underspecifies_accept_use_renderer(self):          request = factory.get('/', HTTP_ACCEPT='*/*') -        accepted_renderer, accepted_media_type = self.negotiate(request) +        accepted_renderer, accepted_media_type = self.select_renderer(request)          self.assertEquals(accepted_media_type, 'application/json')      def test_client_overspecifies_accept_use_client(self):          request = factory.get('/', HTTP_ACCEPT='application/json; indent=8') -        accepted_renderer, accepted_media_type = self.negotiate(request) +        accepted_renderer, accepted_media_type = self.select_renderer(request)          self.assertEquals(accepted_media_type, 'application/json; indent=8') diff --git a/rest_framework/views.py b/rest_framework/views.py index 066c0bb9..357d8939 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -238,7 +238,13 @@ class APIView(View):          """          renderers = self.get_renderers()          conneg = self.get_content_negotiator() -        return conneg.negotiate(request, renderers, self.format_kwarg, force) + +        try: +            return conneg.select_renderer(request, renderers, self.format_kwarg) +        except: +            if force: +                return (renderers[0], renderers[0].media_type) +            raise      def has_permission(self, request, obj=None):          """ | 
