diff --git a/rest_framework/templates/rest_framework/horizontal/select.html b/rest_framework/templates/rest_framework/horizontal/select.html index 3c9e36bbb..216841f06 100644 --- a/rest_framework/templates/rest_framework/horizontal/select.html +++ b/rest_framework/templates/rest_framework/horizontal/select.html @@ -16,7 +16,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% endfor %} diff --git a/rest_framework/templates/rest_framework/horizontal/select_multiple.html b/rest_framework/templates/rest_framework/horizontal/select_multiple.html index 68b40ecc3..83abe19d5 100644 --- a/rest_framework/templates/rest_framework/horizontal/select_multiple.html +++ b/rest_framework/templates/rest_framework/horizontal/select_multiple.html @@ -16,7 +16,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% empty %} diff --git a/rest_framework/templates/rest_framework/inline/select.html b/rest_framework/templates/rest_framework/inline/select.html index 99f10ae71..2796dba24 100644 --- a/rest_framework/templates/rest_framework/inline/select.html +++ b/rest_framework/templates/rest_framework/inline/select.html @@ -15,7 +15,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% endfor %} diff --git a/rest_framework/templates/rest_framework/inline/select_multiple.html b/rest_framework/templates/rest_framework/inline/select_multiple.html index 1c0e96b3c..a9bd08be1 100644 --- a/rest_framework/templates/rest_framework/inline/select_multiple.html +++ b/rest_framework/templates/rest_framework/inline/select_multiple.html @@ -15,7 +15,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% empty %} diff --git a/rest_framework/templates/rest_framework/vertical/select.html b/rest_framework/templates/rest_framework/vertical/select.html index 9736fc072..e9ea536d8 100644 --- a/rest_framework/templates/rest_framework/vertical/select.html +++ b/rest_framework/templates/rest_framework/vertical/select.html @@ -15,7 +15,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% endfor %} diff --git a/rest_framework/templates/rest_framework/vertical/select_multiple.html b/rest_framework/templates/rest_framework/vertical/select_multiple.html index 8d0be0427..65872f369 100644 --- a/rest_framework/templates/rest_framework/vertical/select_multiple.html +++ b/rest_framework/templates/rest_framework/vertical/select_multiple.html @@ -15,7 +15,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% empty %} diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 115f1e186..cb35416e4 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -8,6 +8,10 @@ from django.utils.encoding import force_text from rest_framework.compat import unicode_to_repr +# A singleton helper value to detect unset keyword arguments +unset = object() + + class ReturnDict(OrderedDict): """ Return object from `serializer.data` for the `Serializer` class. @@ -58,10 +62,11 @@ class BoundField(object): providing an API similar to Django forms and form fields. """ - def __init__(self, field, value, errors, prefix=''): + def __init__(self, field, value, errors, prefix='', basic_value=unset): self._field = field self._prefix = prefix self.value = value + self.basic_value = value if basic_value is unset else basic_value self.errors = errors self.name = prefix + self.field_name @@ -79,7 +84,8 @@ class BoundField(object): def as_form_field(self): value = '' if (self.value is None or self.value is False) else force_text(self.value) - return self.__class__(self._field, value, self.errors, self._prefix) + return self.__class__(self._field, value, self.errors, self._prefix, + self.basic_value) class NestedBoundField(BoundField): @@ -89,10 +95,11 @@ class NestedBoundField(BoundField): `BoundField` that is used for serializer fields. """ - def __init__(self, field, value, errors, prefix=''): + def __init__(self, field, value, errors, prefix='', basic_value=unset): if value is None or value is '': value = {} - super(NestedBoundField, self).__init__(field, value, errors, prefix) + super(NestedBoundField, self).__init__(field, value, errors, prefix, + basic_value) def __iter__(self): for field in self.fields.values(): @@ -113,7 +120,8 @@ class NestedBoundField(BoundField): values[key] = value else: values[key] = '' if (value is None or value is False) else force_text(value) - return self.__class__(self._field, values, self.errors, self._prefix) + return self.__class__(self._field, values, self.errors, self._prefix, + self.basic_value) class BindingDict(collections.MutableMapping): diff --git a/tests/test_renderers.py b/tests/test_renderers.py index a947f8b7d..a2620e93c 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -481,3 +481,90 @@ class TestHTMLFormRenderer(TestCase): result = renderer.render(self.serializer.data, None, {}) self.assertIsInstance(result, SafeText) + + +class TestChoiceFieldHTMLFormRenderer(TestCase): + """ + Test rendering ChoiceField with HTMLFormRenderer. + """ + + def setUp(self): + choices = ((1, 'Option1'), (2, 'Option2'), (12, 'Option12')) + + class TestSerializer(serializers.Serializer): + test_field = serializers.ChoiceField(choices=choices, + initial=2) + + self.TestSerializer = TestSerializer + self.renderer = HTMLFormRenderer() + + def test_render_initial_option(self): + serializer = self.TestSerializer() + result = self.renderer.render(serializer.data) + + self.assertIsInstance(result, SafeText) + + self.assertInHTML('', + result) + self.assertInHTML('', result) + self.assertInHTML('', result) + + def test_render_selected_option(self): + serializer = self.TestSerializer(data={'test_field': '12'}) + + serializer.is_valid() + result = self.renderer.render(serializer.data) + + self.assertIsInstance(result, SafeText) + + self.assertInHTML('', + result) + self.assertInHTML('', result) + self.assertInHTML('', result) + + +class TestMultipleChoiceFieldHTMLFormRenderer(TestCase): + """ + Test rendering MultipleChoiceField with HTMLFormRenderer. + """ + + def setUp(self): + self.renderer = HTMLFormRenderer() + + def test_render_selected_option_with_string_option_ids(self): + choices = (('1', 'Option1'), ('2', 'Option2'), ('12', 'Option12'), + ('}', 'OptionBrace')) + + class TestSerializer(serializers.Serializer): + test_field = serializers.MultipleChoiceField(choices=choices) + + serializer = TestSerializer(data={'test_field': ['12']}) + serializer.is_valid() + + result = self.renderer.render(serializer.data) + + self.assertIsInstance(result, SafeText) + + self.assertInHTML('', + result) + self.assertInHTML('', result) + self.assertInHTML('', result) + self.assertInHTML('', result) + + def test_render_selected_option_with_integer_option_ids(self): + choices = ((1, 'Option1'), (2, 'Option2'), (12, 'Option12')) + + class TestSerializer(serializers.Serializer): + test_field = serializers.MultipleChoiceField(choices=choices) + + serializer = TestSerializer(data={'test_field': ['12']}) + serializer.is_valid() + + result = self.renderer.render(serializer.data) + + self.assertIsInstance(result, SafeText) + + self.assertInHTML('', + result) + self.assertInHTML('', result) + self.assertInHTML('', result)