diff options
| -rw-r--r-- | docs/api-guide/permissions.md | 2 | ||||
| -rw-r--r-- | docs/tutorial/2-requests-and-responses.md | 2 | ||||
| -rw-r--r-- | rest_framework/compat.py | 17 | ||||
| -rw-r--r-- | rest_framework/fields.py | 26 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 10 | ||||
| -rw-r--r-- | rest_framework/utils/representation.py | 3 | ||||
| -rw-r--r-- | rest_framework/utils/serializer_helpers.py | 7 | ||||
| -rw-r--r-- | rest_framework/validators.py | 14 | ||||
| -rw-r--r-- | tests/test_fields.py | 4 | ||||
| -rw-r--r-- | tests/test_serializer.py | 6 | 
10 files changed, 64 insertions, 27 deletions
| diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 7558475f..6d86b72c 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -104,7 +104,7 @@ This permission is suitable if you want your API to only be accessible to regist  The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff` is `True` in which case permission will be allowed. -This permission is suitable is you want your API to only be accessible to a subset of trusted administrators. +This permission is suitable if you want your API to only be accessible to a subset of trusted administrators.  ## IsAuthenticatedOrReadOnly diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index f416ed4c..4ca4e264 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -181,7 +181,7 @@ Similarly, we can control the format of the request that we send, using the `Con          "id": 4,          "title": "",          "code": "print 456", -        "linenos": true, +        "linenos": false,          "language": "python",          "style": "friendly"      } diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 43ad6eaa..c5242343 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -16,6 +16,23 @@ from django.utils import six  import django +def unicode_repr(instance): +    # Get the repr of an instance, but ensure it is a unicode string +    # on both python 3 (already the case) and 2 (not the case). +    if six.PY2: +        repr(instance).decode('utf-8') +    return repr(instance) + + +def unicode_to_repr(value): +    # Coerce a unicode string to the correct repr return type, depending on +    # the Python version. We wrap all our `__repr__` implementations with +    # this and then use unicode throughout internally. +    if six.PY2: +        return value.encode('utf-8') +    return value + +  # OrderedDict only available in Python 2.7.  # This will always be the case in Django 1.7 and above, as these versions  # no longer support Python 2.6. diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 205efd2f..f3e17b18 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals  from django.conf import settings  from django.core.exceptions import ObjectDoesNotExist  from django.core.exceptions import ValidationError as DjangoValidationError @@ -10,7 +11,8 @@ from django.utils.translation import ugettext_lazy as _  from rest_framework import ISO_8601  from rest_framework.compat import (      EmailValidator, MinValueValidator, MaxValueValidator, -    MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict +    MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict, +    unicode_repr, unicode_to_repr  )  from rest_framework.exceptions import ValidationError  from rest_framework.settings import api_settings @@ -113,7 +115,9 @@ class CreateOnlyDefault:          return self.default      def __repr__(self): -        return '%s(%s)' % (self.__class__.__name__, repr(self.default)) +        return unicode_to_repr( +            '%s(%s)' % (self.__class__.__name__, unicode_repr(self.default)) +        )  class CurrentUserDefault: @@ -124,7 +128,7 @@ class CurrentUserDefault:          return self.user      def __repr__(self): -        return '%s()' % self.__class__.__name__ +        return unicode_to_repr('%s()' % self.__class__.__name__)  class SkipField(Exception): @@ -382,13 +386,23 @@ class Field(object):          """          Transform the *incoming* primitive data into a native value.          """ -        raise NotImplementedError('to_internal_value() must be implemented.') +        raise NotImplementedError( +            '{cls}.to_internal_value() must be implemented.'.format( +                cls=self.__class__.__name__ +            ) +        )      def to_representation(self, value):          """          Transform the *outgoing* native value into primitive data.          """ -        raise NotImplementedError('to_representation() must be implemented.') +        raise NotImplementedError( +            '{cls}.to_representation() must be implemented.\n' +            'If you are upgrading from REST framework version 2 ' +            'you might want `ReadOnlyField`.'.format( +                cls=self.__class__.__name__ +            ) +        )      def fail(self, key, **kwargs):          """ @@ -453,7 +467,7 @@ class Field(object):          This allows us to create descriptive representations for serializer          instances that show all the declared fields on the serializer.          """ -        return representation.field_repr(self) +        return unicode_to_repr(representation.field_repr(self))  # Boolean types... diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5adbca3b..e9860a2f 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -10,12 +10,11 @@ python primitives.  2. The process of marshalling between python primitives and request and  response content is handled by parsers and renderers.  """ -import warnings - +from __future__ import unicode_literals  from django.db import models  from django.db.models.fields import FieldDoesNotExist  from django.utils.translation import ugettext_lazy as _ - +from rest_framework.compat import unicode_to_repr  from rest_framework.utils import model_meta  from rest_framework.utils.field_mapping import (      get_url_kwargs, get_field_kwargs, @@ -29,6 +28,7 @@ from rest_framework.validators import (      UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator,      UniqueTogetherValidator  ) +import warnings  # Note: We do the following so that users of the framework can use this style: @@ -396,7 +396,7 @@ class Serializer(BaseSerializer):          return attrs      def __repr__(self): -        return representation.serializer_repr(self, indent=1) +        return unicode_to_repr(representation.serializer_repr(self, indent=1))      # The following are used for accessing `BoundField` instances on the      # serializer, for the purposes of presenting a form-like API onto the @@ -564,7 +564,7 @@ class ListSerializer(BaseSerializer):          return self.instance      def __repr__(self): -        return representation.list_repr(self, indent=1) +        return unicode_to_repr(representation.list_repr(self, indent=1))      # Include a backlink to the serializer class on return objects.      # Allows renderers such as HTMLFormRenderer to get the full field info. diff --git a/rest_framework/utils/representation.py b/rest_framework/utils/representation.py index 0fdb4775..1bfc64c1 100644 --- a/rest_framework/utils/representation.py +++ b/rest_framework/utils/representation.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals  from django.db import models  from django.utils.encoding import force_text  from django.utils.functional import Promise +from rest_framework.compat import unicode_repr  import re @@ -25,7 +26,7 @@ def smart_repr(value):      if isinstance(value, Promise) and value._delegate_text:          value = force_text(value) -    value = repr(value).decode('utf-8') +    value = unicode_repr(value)      # Representations like u'help text'      # should simply be presented as 'help text' diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 277cf649..65a04d06 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,5 +1,6 @@ +from __future__ import unicode_literals  import collections -from rest_framework.compat import OrderedDict +from rest_framework.compat import OrderedDict, unicode_to_repr  class ReturnDict(OrderedDict): @@ -47,9 +48,9 @@ class BoundField(object):          return self._field.__class__      def __repr__(self): -        return '<%s value=%s errors=%s>' % ( +        return unicode_to_repr('<%s value=%s errors=%s>' % (              self.__class__.__name__, self.value, self.errors -        ) +        ))  class NestedBoundField(BoundField): diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 63eb7b22..e3719b8d 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -6,7 +6,9 @@ This gives us better separation of concerns, allows us to use single-step  object creation, and makes it possible to switch between using the implicit  `ModelSerializer` class and an equivalent explicit `Serializer` class.  """ +from __future__ import unicode_literals  from django.utils.translation import ugettext_lazy as _ +from rest_framework.compat import unicode_to_repr  from rest_framework.exceptions import ValidationError  from rest_framework.utils.representation import smart_repr @@ -59,10 +61,10 @@ class UniqueValidator:              raise ValidationError(self.message)      def __repr__(self): -        return '<%s(queryset=%s)>' % ( +        return unicode_to_repr('<%s(queryset=%s)>' % (              self.__class__.__name__,              smart_repr(self.queryset) -        ) +        ))  class UniqueTogetherValidator: @@ -141,11 +143,11 @@ class UniqueTogetherValidator:              raise ValidationError(self.message.format(field_names=field_names))      def __repr__(self): -        return '<%s(queryset=%s, fields=%s)>' % ( +        return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % (              self.__class__.__name__,              smart_repr(self.queryset),              smart_repr(self.fields) -        ) +        ))  class BaseUniqueForValidator: @@ -205,12 +207,12 @@ class BaseUniqueForValidator:              raise ValidationError({self.field: message})      def __repr__(self): -        return '<%s(queryset=%s, field=%s, date_field=%s)>' % ( +        return unicode_to_repr('<%s(queryset=%s, field=%s, date_field=%s)>' % (              self.__class__.__name__,              smart_repr(self.queryset),              smart_repr(self.field),              smart_repr(self.date_field) -        ) +        ))  class UniqueForDateValidator(BaseUniqueForValidator): diff --git a/tests/test_fields.py b/tests/test_fields.py index 3f4e65f2..c20bdd8c 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -62,7 +62,7 @@ class TestEmpty:          """          field = serializers.CharField(allow_blank=True)          output = field.run_validation('') -        assert output is '' +        assert output == ''      def test_default(self):          """ @@ -817,7 +817,7 @@ class TestChoiceField(FieldValues):              ]          )          output = field.run_validation('') -        assert output is '' +        assert output == ''  class TestChoiceFieldWithType(FieldValues): diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 48fcc83b..c17b6d8c 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1,6 +1,7 @@  # coding: utf-8  from __future__ import unicode_literals  from rest_framework import serializers +from rest_framework.compat import unicode_repr  import pytest @@ -208,9 +209,10 @@ class TestUnicodeRepr:          class ExampleObject:              def __init__(self):                  self.example = '한국' +              def __repr__(self): -                return self.example.encode('utf8') +                return unicode_repr(self.example)          instance = ExampleObject()          serializer = ExampleSerializer(instance) -        repr(serializer) +        repr(serializer)  # Should not error. | 
