aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2014-12-15 12:18:55 +0000
committerTom Christie2014-12-15 12:18:55 +0000
commitb6ee784240b3c7f6cd62af5b6fe6d1014d7bf6d4 (patch)
treee3cdb5a6e210e5ba26d6726cc08a44c8f450776b
parent8934e61b67e4aed38b04f2fe18f011ecbf9010cb (diff)
parentaf53e34dd5873f3373e9991c3825e70d92432e14 (diff)
downloaddjango-rest-framework-b6ee784240b3c7f6cd62af5b6fe6d1014d7bf6d4.tar.bz2
Merge master
-rw-r--r--docs/api-guide/permissions.md2
-rw-r--r--docs/tutorial/2-requests-and-responses.md2
-rw-r--r--rest_framework/compat.py17
-rw-r--r--rest_framework/fields.py26
-rw-r--r--rest_framework/serializers.py10
-rw-r--r--rest_framework/utils/representation.py3
-rw-r--r--rest_framework/utils/serializer_helpers.py7
-rw-r--r--rest_framework/validators.py14
-rw-r--r--tests/test_fields.py4
-rw-r--r--tests/test_serializer.py6
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.