diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 3f8a36e2a..8c28273b0 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -199,10 +199,16 @@ If you want to override this behavior, you'll need to declare the `DateTimeField class CommentSerializer(serializers.ModelSerializer): created = serializers.DateTimeField() - + class Meta: model = Comment +## TimeField + +A time representation. + +Corresponds to `django.db.models.fields.TimeField` + ## IntegerField An integer representation. diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 9636b9c17..3fd865f85 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -349,7 +349,7 @@ except ImportError: # dateparse is ALSO new in Django 1.4 try: - from django.utils.dateparse import parse_date, parse_datetime + from django.utils.dateparse import parse_date, parse_datetime, parse_time except ImportError: import datetime import re diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 327008fb1..236e0f1e5 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -18,6 +18,7 @@ from rest_framework.compat import timezone from rest_framework.compat import BytesIO from rest_framework.compat import six from rest_framework.compat import smart_text +from rest_framework.compat import parse_time def is_simple_callable(obj): @@ -531,6 +532,33 @@ class DateTimeField(WritableField): raise ValidationError(msg) +class TimeField(WritableField): + type_name = 'TimeField' + widget = widgets.TimeInput + form_field_class = forms.TimeField + + default_error_messages = { + 'invalid': _("'%s' value has an invalid format. It must be a valid " + "time in the HH:MM[:ss[.uuuuuu]] format."), + } + empty = None + + def from_native(self, value): + if value in validators.EMPTY_VALUES: + return None + + if isinstance(value, datetime.time): + return value + + try: + parsed = parse_time(value) + assert parsed is not None + return parsed + except ValueError: + msg = self.error_messages['invalid'] % value + raise ValidationError(msg) + + class IntegerField(WritableField): type_name = 'IntegerField' form_field_class = forms.IntegerField diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5d3475d41..b0372ab83 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -517,6 +517,7 @@ class ModelSerializer(Serializer): models.PositiveSmallIntegerField: IntegerField, models.DateTimeField: DateTimeField, models.DateField: DateField, + models.TimeField: TimeField, models.EmailField: EmailField, models.CharField: CharField, models.URLField: URLField, diff --git a/rest_framework/tests/fields.py b/rest_framework/tests/fields.py index b7587bf14..34f616781 100644 --- a/rest_framework/tests/fields.py +++ b/rest_framework/tests/fields.py @@ -2,8 +2,10 @@ General serializer field tests. """ from __future__ import unicode_literals +import datetime from django.db import models from django.test import TestCase +from django.core import validators from rest_framework import serializers @@ -26,7 +28,16 @@ class CharPrimaryKeyModelSerializer(serializers.ModelSerializer): model = CharPrimaryKeyModel -class ReadOnlyFieldTests(TestCase): +class TimeFieldModel(models.Model): + clock = models.TimeField() + + +class TimeFieldModelSerializer(serializers.ModelSerializer): + class Meta: + model = TimeFieldModel + + +class BasicFieldTests(TestCase): def test_auto_now_fields_read_only(self): """ auto_now and auto_now_add fields should be read_only by default. @@ -47,3 +58,38 @@ class ReadOnlyFieldTests(TestCase): """ serializer = CharPrimaryKeyModelSerializer() self.assertEquals(serializer.fields['id'].read_only, False) + + def test_TimeField_from_native(self): + f = serializers.TimeField() + result = f.from_native('12:34:56.987654') + + self.assertEqual(datetime.time(12, 34, 56, 987654), result) + + def test_TimeField_from_native_datetime_time(self): + """ + Make sure from_native() accepts a datetime.time instance. + """ + f = serializers.TimeField() + result = f.from_native(datetime.time(12, 34, 56)) + self.assertEqual(result, datetime.time(12, 34, 56)) + + def test_TimeField_from_native_empty(self): + f = serializers.TimeField() + result = f.from_native('') + self.assertEqual(result, None) + + def test_TimeField_from_native_invalid_time(self): + f = serializers.TimeField() + + try: + f.from_native('12:69:12') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["'12:69:12' value has an invalid " + "format. It must be a valid time " + "in the HH:MM[:ss[.uuuuuu]] format."]) + else: + self.fail("ValidationError was not properly raised") + + def test_TimeFieldModelSerializer(self): + serializer = TimeFieldModelSerializer() + self.assertTrue(isinstance(serializer.fields['clock'], serializers.TimeField))