diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index f043e6327..4fa0b1e78 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -34,6 +34,7 @@ from rest_framework.settings import api_settings from rest_framework.utils import encoders, json from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.utils.field_mapping import ClassLookupDict +from rest_framework.utils.serializer_helpers import ReturnDict def zero_as_none(value): @@ -522,12 +523,14 @@ class BrowsableAPIRenderer(BaseRenderer): return self.render_form_for_serializer(serializer) def render_form_for_serializer(self, serializer): - if hasattr(serializer, 'initial_data'): - serializer.is_valid() + if hasattr(serializer, 'initial_data') and not serializer.is_valid(): + data = ReturnDict(serializer.get_initial(), serializer=serializer) + else: + data = serializer.data form_renderer = self.form_renderer_class() return form_renderer.render( - serializer.data, + data, self.accepted_media_type, {'style': {'template_pack': 'rest_framework/horizontal'}} ) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9830edb3f..e8f1ac1b0 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -262,6 +262,9 @@ class BaseSerializer(Field): self._data = self.to_representation(self.instance) elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): self._data = self.to_representation(self.validated_data) + elif hasattr(self, '_validated_data') and getattr(self, '_errors', None): + msg = 'You can not access `.data` in an invalid serializer.' + raise AssertionError(msg) else: self._data = self.get_initial() return self._data @@ -547,7 +550,13 @@ class Serializer(BaseSerializer): def __getitem__(self, key): field = self.fields[key] - value = self.data.get(key) + + # If the data is not valid, return the initial data + if not hasattr(self, '_data') and hasattr(self, '_validated_data') and getattr(self, '_errors', None): + value = self.get_initial().get(key) + else: + value = self.data.get(key) + error = self.errors.get(key) if hasattr(self, '_errors') else None if isinstance(field, Serializer): return NestedBoundField(field, value, error) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index f48675d5e..371d2c194 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -13,6 +13,7 @@ from django.utils.safestring import SafeData, mark_safe from rest_framework.compat import apply_markdown, pygments_highlight from rest_framework.renderers import HTMLFormRenderer +from rest_framework.utils.serializer_helpers import ReturnDict from rest_framework.utils.urls import replace_query_param register = template.Library() @@ -79,9 +80,14 @@ def get_pagination_html(pager): @register.simple_tag def render_form(serializer, template_pack=None): + if hasattr(serializer, 'initial_data') and not serializer.is_valid(): + data = ReturnDict(serializer.get_initial(), serializer=serializer) + else: + data = serializer.data + style = {'template_pack': template_pack} if template_pack else {} renderer = HTMLFormRenderer() - return renderer.render(serializer.data, None, {'style': style}) + return renderer.render(data, None, {'style': style}) @register.simple_tag diff --git a/tests/test_renderers.py b/tests/test_renderers.py index b4c41b148..21a6c7d68 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -499,7 +499,7 @@ class TestHTMLFormRenderer(TestCase): test_field = serializers.CharField() self.renderer = HTMLFormRenderer() - self.serializer = TestSerializer(data={}) + self.serializer = TestSerializer(data={'test_field': 'test'}) def test_render_with_default_args(self): self.serializer.is_valid() diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 0f1e81965..50732ef87 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -86,15 +86,17 @@ class TestSerializer: serializer = self.Serializer(data={'char': 'abc'}) assert not serializer.is_valid() assert serializer.validated_data == {} - assert serializer.data == {'char': 'abc'} assert serializer.errors == {'integer': ['This field is required.']} + with pytest.raises(AssertionError): + serializer.data def test_invalid_datatype(self): serializer = self.Serializer(data=[{'char': 'abc'}]) assert not serializer.is_valid() assert serializer.validated_data == {} - assert serializer.data == {} assert serializer.errors == {'non_field_errors': ['Invalid data. Expected a dictionary, but got list.']} + with pytest.raises(AssertionError): + serializer.data def test_partial_validation(self): serializer = self.Serializer(data={'char': 'abc'}, partial=True) @@ -208,11 +210,12 @@ class TestSerializer: serializer = ExampleSerializer(data=data) assert not serializer.is_valid() - assert serializer.data == data assert serializer.validated_data == {} assert serializer.errors == {'char': [ exceptions.ErrorDetail(string='Raised error', code='invalid') ]} + with pytest.raises(AssertionError): + serializer.data class TestValidateMethod: