aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/api-guide/fields.md4
-rw-r--r--docs/api-guide/serializers.md6
-rw-r--r--rest_framework/exceptions.py33
-rw-r--r--rest_framework/relations.py15
-rw-r--r--rest_framework/request.py2
-rw-r--r--rest_framework/tests/test_relations.py20
6 files changed, 44 insertions, 36 deletions
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index e05c0306..91866664 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -167,13 +167,13 @@ or `django.db.models.fields.TextField`.
Corresponds to `django.db.models.fields.URLField`. Uses Django's `django.core.validators.URLValidator` for validation.
-**Signature:** `CharField(max_length=200, min_length=None)`
+**Signature:** `URLField(max_length=200, min_length=None)`
## SlugField
Corresponds to `django.db.models.fields.SlugField`.
-**Signature:** `CharField(max_length=50, min_length=None)`
+**Signature:** `SlugField(max_length=50, min_length=None)`
## ChoiceField
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 6fc25f57..04439092 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -103,11 +103,11 @@ Deserialization is similar. First we parse a stream into Python native datatype
When deserializing data, we can either create a new instance, or update an existing instance.
serializer = CommentSerializer(data=data) # Create new instance
- serializer = CommentSerializer(comment, data=data) # Update `instance`
+ serializer = CommentSerializer(comment, data=data) # Update `comment`
By default, serializers must be passed values for all required fields or they will throw validation errors. You can use the `partial` argument in order to allow partial updates.
- serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True) # Update `instance` with partial data
+ serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True) # Update `comment` with partial data
## Validation
@@ -208,7 +208,7 @@ Similarly if a nested representation should be a list of items, you should pass
Validation of nested objects will work the same as before. Errors with nested objects will be nested under the field name of the nested object.
- serializer = CommentSerializer(comment, data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
+ serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py
index 425a7214..4276625a 100644
--- a/rest_framework/exceptions.py
+++ b/rest_framework/exceptions.py
@@ -6,6 +6,7 @@ In addition Django's built in 403 and 404 exceptions are handled.
"""
from __future__ import unicode_literals
from rest_framework import status
+import math
class APIException(Exception):
@@ -13,40 +14,32 @@ class APIException(Exception):
Base class for REST framework exceptions.
Subclasses should provide `.status_code` and `.detail` properties.
"""
- pass
+ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
+ default_detail = ''
+
+ def __init__(self, detail=None):
+ self.detail = detail or self.default_detail
class ParseError(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = 'Malformed request.'
- def __init__(self, detail=None):
- self.detail = detail or self.default_detail
-
class AuthenticationFailed(APIException):
status_code = status.HTTP_401_UNAUTHORIZED
default_detail = 'Incorrect authentication credentials.'
- def __init__(self, detail=None):
- self.detail = detail or self.default_detail
-
class NotAuthenticated(APIException):
status_code = status.HTTP_401_UNAUTHORIZED
default_detail = 'Authentication credentials were not provided.'
- def __init__(self, detail=None):
- self.detail = detail or self.default_detail
-
class PermissionDenied(APIException):
status_code = status.HTTP_403_FORBIDDEN
default_detail = 'You do not have permission to perform this action.'
- def __init__(self, detail=None):
- self.detail = detail or self.default_detail
-
class MethodNotAllowed(APIException):
status_code = status.HTTP_405_METHOD_NOT_ALLOWED
@@ -75,14 +68,14 @@ class UnsupportedMediaType(APIException):
class Throttled(APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
- default_detail = "Request was throttled."
+ default_detail = 'Request was throttled.'
extra_detail = "Expected available in %d second%s."
def __init__(self, wait=None, detail=None):
- import math
- self.wait = wait and math.ceil(wait) or None
- if wait is not None:
- format = detail or self.default_detail + self.extra_detail
- self.detail = format % (self.wait, self.wait != 1 and 's' or '')
- else:
+ if wait is None:
self.detail = detail or self.default_detail
+ self.wait = None
+ else:
+ format = (detail or self.default_detail) + self.extra_detail
+ self.detail = format % (wait, wait != 1 and 's' or '')
+ self.wait = math.ceil(wait)
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index 35c00bf1..02185c2f 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -65,16 +65,11 @@ class RelatedField(WritableField):
def initialize(self, parent, field_name):
super(RelatedField, self).initialize(parent, field_name)
if self.queryset is None and not self.read_only:
- try:
- manager = getattr(self.parent.opts.model, self.source or field_name)
- if hasattr(manager, 'related'): # Forward
- self.queryset = manager.related.model._default_manager.all()
- else: # Reverse
- self.queryset = manager.field.rel.to._default_manager.all()
- except Exception:
- msg = ('Serializer related fields must include a `queryset`' +
- ' argument or set `read_only=True')
- raise Exception(msg)
+ manager = getattr(self.parent.opts.model, self.source or field_name)
+ if hasattr(manager, 'related'): # Forward
+ self.queryset = manager.related.model._default_manager.all()
+ else: # Reverse
+ self.queryset = manager.field.rel.to._default_manager.all()
### We need this stuff to make form choices work...
diff --git a/rest_framework/request.py b/rest_framework/request.py
index fcea2508..d1f04ec5 100644
--- a/rest_framework/request.py
+++ b/rest_framework/request.py
@@ -223,7 +223,7 @@ class Request(object):
def user(self, value):
"""
Sets the user on the current request. This is necessary to maintain
- compatilbility with django.contrib.auth where the user proprety is
+ compatibility with django.contrib.auth where the user property is
set in the login and logout functions.
"""
self._user = value
diff --git a/rest_framework/tests/test_relations.py b/rest_framework/tests/test_relations.py
index d19219c9..f52e0e1e 100644
--- a/rest_framework/tests/test_relations.py
+++ b/rest_framework/tests/test_relations.py
@@ -98,3 +98,23 @@ class RelatedFieldSourceTests(TestCase):
obj = ClassWithQuerysetMethod()
value = field.field_to_native(obj, 'field_name')
self.assertEqual(value, ['BlogPost object'])
+
+ # Regression for #1129
+ def test_exception_for_incorect_fk(self):
+ """
+ Check that the exception message are correct if the source field
+ doesn't exist.
+ """
+ from rest_framework.tests.models import ManyToManySource
+ class Meta:
+ model = ManyToManySource
+ attrs = {
+ 'name': serializers.SlugRelatedField(
+ slug_field='name', source='banzai'),
+ 'Meta': Meta,
+ }
+
+ TestSerializer = type(str('TestSerializer'),
+ (serializers.ModelSerializer,), attrs)
+ with self.assertRaises(AttributeError):
+ TestSerializer(data={'name': 'foo'})