diff options
| -rw-r--r-- | docs/api-guide/renderers.md | 9 | ||||
| -rw-r--r-- | rest_framework/renderers.py | 17 | ||||
| -rw-r--r-- | rest_framework/routers.py | 6 | ||||
| -rw-r--r-- | rest_framework/tests/test_routers.py | 2 | 
4 files changed, 23 insertions, 11 deletions
diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 7fc1fc1f..d46d0568 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -88,7 +88,7 @@ The client may additionally include an `'indent'` media type parameter, in which  **.format**: `'.json'` -**.charset**: `utf-8` +**.charset**: `None`  ## UnicodeJSONRenderer @@ -110,7 +110,7 @@ Both the `JSONRenderer` and `UnicodeJSONRenderer` styles conform to [RFC 4627][r  **.format**: `'.json'` -**.charset**: `utf-8` +**.charset**: `None`  ## JSONPRenderer @@ -295,12 +295,15 @@ By default renderer classes are assumed to be using the `UTF-8` encoding.  To us  Note that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the `Response` class, with the `charset` attribute set on the renderer used to determine the encoding. -If the renderer returns a bytestring representing raw binary content, you should set a charset value of `None`, which will ensure the `Content-Type` header of the response will not have a `charset` value set.  Doing so will also ensure that the browsable API will not attempt to display the binary content as a string. +If the renderer returns a bytestring representing raw binary content, you should set a charset value of `None`, which will ensure the `Content-Type` header of the response will not have a `charset` value set. + +In some cases you may also want to set the `render_style` attribute to `'binary'`.  Doing so will also ensure that the browsable API will not attempt to display the binary content as a string.      class JPEGRenderer(renderers.BaseRenderer):          media_type = 'image/jpeg'          format = 'jpg'          charset = None +        render_style = 'binary'          def render(self, data, media_type=None, renderer_context=None):              return data diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 9885c8dd..c07b1652 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -36,6 +36,7 @@ class BaseRenderer(object):      media_type = None      format = None      charset = 'utf-8' +    render_style = 'text'      def render(self, data, accepted_media_type=None, renderer_context=None):          raise NotImplemented('Renderer class requires .render() to be implemented') @@ -51,16 +52,17 @@ class JSONRenderer(BaseRenderer):      format = 'json'      encoder_class = encoders.JSONEncoder      ensure_ascii = True -    charset = 'utf-8' -    # Note that JSON encodings must be utf-8, utf-16 or utf-32. +    charset = None +    # JSON is a binary encoding, that can be encoded as utf-8, utf-16 or utf-32.      # See: http://www.ietf.org/rfc/rfc4627.txt +    # Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/      def render(self, data, accepted_media_type=None, renderer_context=None):          """          Render `data` into JSON.          """          if data is None: -            return '' +            return bytes()          # If 'indent' is provided in the context, then pretty print the result.          # E.g. If we're being called by the BrowsableAPIRenderer. @@ -85,13 +87,12 @@ class JSONRenderer(BaseRenderer):          # and may (or may not) be unicode.          # On python 3.x json.dumps() returns unicode strings.          if isinstance(ret, six.text_type): -            return bytes(ret.encode(self.charset)) +            return bytes(ret.encode('utf-8'))          return ret  class UnicodeJSONRenderer(JSONRenderer):      ensure_ascii = False -    charset = 'utf-8'      """      Renderer which serializes to JSON.      Does *not* apply JSON's character escaping for non-ascii characters. @@ -108,6 +109,7 @@ class JSONPRenderer(JSONRenderer):      format = 'jsonp'      callback_parameter = 'callback'      default_callback = 'callback' +    charset = 'utf-8'      def get_callback(self, renderer_context):          """ @@ -414,7 +416,10 @@ class BrowsableAPIRenderer(BaseRenderer):          renderer_context['indent'] = 4          content = renderer.render(data, accepted_media_type, renderer_context) -        if renderer.charset is None: +        render_style = getattr(renderer, 'render_style', 'text') +        assert render_style in ['text', 'binary'], 'Expected .render_style ' \ +            '"text" or "binary", but got "%s"' % render_style +        if render_style == 'binary':              return '[%d bytes of binary content]' % len(content)          return content diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 930011d3..3fee1e49 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -189,7 +189,11 @@ class SimpleRouter(BaseRouter):          Given a viewset, return the portion of URL regex that is used          to match against a single instance.          """ -        base_regex = '(?P<{lookup_field}>[^/]+)' +        if self.trailing_slash: +            base_regex = '(?P<{lookup_field}>[^/]+)' +        else: +            # Don't consume `.json` style suffixes +            base_regex = '(?P<{lookup_field}>[^/.]+)'          lookup_field = getattr(viewset, 'lookup_field', 'pk')          return base_regex.format(lookup_field=lookup_field) diff --git a/rest_framework/tests/test_routers.py b/rest_framework/tests/test_routers.py index 5fcccb74..e723f7d4 100644 --- a/rest_framework/tests/test_routers.py +++ b/rest_framework/tests/test_routers.py @@ -146,7 +146,7 @@ class TestTrailingSlashRemoved(TestCase):          self.urls = self.router.urls      def test_urls_can_have_trailing_slash_removed(self): -        expected = ['^notes$', '^notes/(?P<pk>[^/]+)$'] +        expected = ['^notes$', '^notes/(?P<pk>[^/.]+)$']          for idx in range(len(expected)):              self.assertEqual(expected[idx], self.urls[idx].regex.pattern)  | 
