Repository: encode/django-rest-framework

Issue:
allow DateTimeFields to accept strings as input
I found an interesting "bug" with Django Rest Framework along with how Django's DateTimeField works. Given the following TestCase:

```
class ReadOnlyDateTimeModel(models.Model):
    date = models.DateTimeField()

    def save(self, *args, **kwargs):
        self.date = '2015-02-03 00:00:00'
        return super(ReadOnlyDateTimeModel, self).save(*args, **kwargs)


class ReadOnlyDateTimeSerializer(serializers.ModelSerializer):
    date = serializers.DateTimeField(read_only=True)

    class Meta:
        model = ReadOnlyDateTimeModel


class ReadOnlyDateTimeView(generics.ListCreateAPIView):
    queryset = ReadOnlyDateTimeModel.objects.all()
    serializer_class = ReadOnlyDateTimeSerializer


class TestReadOnlyFieldSerializer(TestCase):
    def setUp(self):
        self.view = ReadOnlyDateTimeView.as_view()

    def test_model_save_works(self):
        obj = ReadOnlyDateTimeModel.objects.create()
        assert obj.date == '2015-02-03 00:00:00'

    def test_post_root_view(self):
        """
        POST requests to ListCreateAPIView should create a new object.
        """
        data = {'price': 1}
        request = factory.post('/', data, format='json')
        response = self.view(request).render()
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
```

I see this error:

```
tests/test_generics.py:544: in test_post_root_view
    response = self.view(request).render()
/usr/local/lib/python2.7/site-packages/django/views/decorators/csrf.py:57: in wrapped_view
    return view_func(*args, **kwargs)
/usr/local/lib/python2.7/site-packages/django/views/generic/base.py:69: in view
    return self.dispatch(request, *args, **kwargs)
rest_framework/views.py:452: in dispatch
    response = self.handle_exception(exc)
rest_framework/views.py:449: in dispatch
    response = handler(request, *args, **kwargs)
rest_framework/generics.py:244: in post
    return self.create(request, *args, **kwargs)
rest_framework/mixins.py:21: in create
    headers = self.get_success_headers(serializer.data)
rest_framework/serializers.py:466: in data
    ret = super(Serializer, self).data
rest_framework/serializers.py:213: in data
    self._data = self.to_representation(self.instance)
rest_framework/serializers.py:435: in to_representation
    ret[field.field_name] = field.to_representation(attribute)
rest_framework/fields.py:879: in to_representation
    value = value.isoformat()
E   AttributeError: 'unicode' object has no attribute 'isoformat'
```

It seems like Django will accept a formatted string as input, however DRF tries to format the field as a datetime rather than a string, causing the above failure case. If I change `self.date = '2015-02-03 00:00:00'` to `self.date = datetime.datetime.now()`, it all works as expected.



Discussion:
Yup, we probably should correctly coerce string values too.
Really the internal value _ought_ to be a `datetime` instance rather than a raw string, but we should still adopt the more graceful stance.


Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -925,6 +925,9 @@ def to_internal_value(self, value):
         self.fail('invalid', format=humanized_format)
 
     def to_representation(self, value):
+        if not value:
+            return None
+
         if self.format is None:
             return value
 
@@ -938,7 +941,10 @@ def to_representation(self, value):
         )
 
         if self.format.lower() == ISO_8601:
+            if (isinstance(value, str)):
+                value = datetime.datetime.strptime(value, '%Y-%m-%d').date()
             return value.isoformat()
+
         return value.strftime(self.format)
 
 


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -726,7 +726,10 @@ class TestDateField(FieldValues):
         datetime.datetime(2001, 1, 1, 12, 00): ['Expected a date but got a datetime.'],
     }
     outputs = {
-        datetime.date(2001, 1, 1): '2001-01-01'
+        datetime.date(2001, 1, 1): '2001-01-01',
+        '2001-01-01': '2001-01-01',
+        None: None,
+        '': None,
     }
     field = serializers.DateField()