aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/authtoken/serializers.py11
-rw-r--r--rest_framework/fields.py2
-rw-r--r--rest_framework/serializers.py9
-rw-r--r--rest_framework/tests/models.py3
-rw-r--r--rest_framework/tests/test_fields.py4
-rw-r--r--rest_framework/tests/test_generics.py74
6 files changed, 96 insertions, 7 deletions
diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py
index 60a3740e..99e99ae3 100644
--- a/rest_framework/authtoken/serializers.py
+++ b/rest_framework/authtoken/serializers.py
@@ -1,4 +1,6 @@
from django.contrib.auth import authenticate
+from django.utils.translation import ugettext_lazy as _
+
from rest_framework import serializers
@@ -15,10 +17,13 @@ class AuthTokenSerializer(serializers.Serializer):
if user:
if not user.is_active:
- raise serializers.ValidationError('User account is disabled.')
+ msg = _('User account is disabled.')
+ raise serializers.ValidationError(msg)
attrs['user'] = user
return attrs
else:
- raise serializers.ValidationError('Unable to login with provided credentials.')
+ msg = _('Unable to login with provided credentials.')
+ raise serializers.ValidationError(msg)
else:
- raise serializers.ValidationError('Must include "username" and "password"')
+ msg = _('Must include "username" and "password"')
+ raise serializers.ValidationError(msg)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index e6733849..d80aab56 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -62,7 +62,7 @@ def get_component(obj, attr_name):
def readable_datetime_formats(formats):
format = ', '.join(formats).replace(ISO_8601,
- 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]')
+ 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]')
return humanize_strptime(format)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 9cb548a5..2a0d5263 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -833,6 +833,15 @@ class ModelSerializer(Serializer):
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name
+ if not model_field.editable:
+ kwargs['read_only'] = True
+
+ if model_field.verbose_name is not None:
+ kwargs['label'] = model_field.verbose_name
+
+ if model_field.help_text is not None:
+ kwargs['help_text'] = model_field.help_text
+
return PrimaryKeyRelatedField(**kwargs)
def get_field(self, model_field):
diff --git a/rest_framework/tests/models.py b/rest_framework/tests/models.py
index 0256697a..e171d3bd 100644
--- a/rest_framework/tests/models.py
+++ b/rest_framework/tests/models.py
@@ -151,7 +151,8 @@ class ForeignKeySource(RESTFrameworkModel):
class NullableForeignKeySource(RESTFrameworkModel):
name = models.CharField(max_length=100)
target = models.ForeignKey(ForeignKeyTarget, null=True, blank=True,
- related_name='nullable_sources')
+ related_name='nullable_sources',
+ verbose_name='Optional target object')
# OneToOne
diff --git a/rest_framework/tests/test_fields.py b/rest_framework/tests/test_fields.py
index 03f79cf4..3ae1c438 100644
--- a/rest_framework/tests/test_fields.py
+++ b/rest_framework/tests/test_fields.py
@@ -323,7 +323,7 @@ class DateTimeFieldTest(TestCase):
f.from_native('04:61:59')
except validators.ValidationError as e:
self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: "
- "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]"])
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]"])
else:
self.fail("ValidationError was not properly raised")
@@ -337,7 +337,7 @@ class DateTimeFieldTest(TestCase):
f.from_native('04 -- 31')
except validators.ValidationError as e:
self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: "
- "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]"])
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]"])
else:
self.fail("ValidationError was not properly raised")
diff --git a/rest_framework/tests/test_generics.py b/rest_framework/tests/test_generics.py
index 996bd5b0..57d327cc 100644
--- a/rest_framework/tests/test_generics.py
+++ b/rest_framework/tests/test_generics.py
@@ -5,6 +5,7 @@ from django.test import TestCase
from rest_framework import generics, renderers, serializers, status
from rest_framework.test import APIRequestFactory
from rest_framework.tests.models import BasicModel, Comment, SlugBasedModel
+from rest_framework.tests.models import ForeignKeySource, ForeignKeyTarget
from rest_framework.compat import six
factory = APIRequestFactory()
@@ -28,6 +29,13 @@ class InstanceView(generics.RetrieveUpdateDestroyAPIView):
return queryset.exclude(text='filtered out')
+class FKInstanceView(generics.RetrieveUpdateDestroyAPIView):
+ """
+ FK: example description for OPTIONS.
+ """
+ model = ForeignKeySource
+
+
class SlugSerializer(serializers.ModelSerializer):
slug = serializers.Field() # read only
@@ -407,6 +415,72 @@ class TestInstanceView(TestCase):
self.assertFalse(self.objects.filter(id=999).exists())
+class TestFKInstanceView(TestCase):
+ def setUp(self):
+ """
+ Create 3 BasicModel instances.
+ """
+ items = ['foo', 'bar', 'baz']
+ for item in items:
+ t = ForeignKeyTarget(name=item)
+ t.save()
+ ForeignKeySource(name='source_' + item, target=t).save()
+
+ self.objects = ForeignKeySource.objects
+ self.data = [
+ {'id': obj.id, 'name': obj.name}
+ for obj in self.objects.all()
+ ]
+ self.view = FKInstanceView.as_view()
+
+ def test_options_root_view(self):
+ """
+ OPTIONS requests to ListCreateAPIView should return metadata
+ """
+ request = factory.options('/999')
+ with self.assertNumQueries(1):
+ response = self.view(request, pk=999).render()
+ expected = {
+ 'name': 'Fk Instance',
+ 'description': 'FK: example description for OPTIONS.',
+ 'renders': [
+ 'application/json',
+ 'text/html'
+ ],
+ 'parses': [
+ 'application/json',
+ 'application/x-www-form-urlencoded',
+ 'multipart/form-data'
+ ],
+ 'actions': {
+ 'PUT': {
+ 'id': {
+ 'type': 'integer',
+ 'required': False,
+ 'read_only': True,
+ 'label': 'ID'
+ },
+ 'name': {
+ 'type': 'string',
+ 'required': True,
+ 'read_only': False,
+ 'label': 'name',
+ 'max_length': 100
+ },
+ 'target': {
+ 'type': 'field',
+ 'required': True,
+ 'read_only': False,
+ 'label': 'Target',
+ 'help_text': 'Target'
+ }
+ }
+ }
+ }
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data, expected)
+
+
class TestOverriddenGetObject(TestCase):
"""
Test cases for a RetrieveUpdateDestroyAPIView that does NOT use the