Repository: encode/django-rest-framework

Issue:
Improve HTML form rendering of choice fields
Another take on fixing choice field HTML rendering issues (#4120 and #4121) - this is a smaller impact patch, as suggested in #4122.



Discussion:

Resolving patch:
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -89,6 +89,21 @@ def add_query_param(request, key, val):
     return escape(replace_query_param(uri, key, val))
 
 
+@register.filter
+def as_string(value):
+    if value is None:
+        return ''
+    return '%s' % value
+
+
+@register.filter
+def as_list_of_strings(value):
+    return [
+        '' if (item is None) else ('%s' % item)
+        for item in value
+    ]
+
+
 @register.filter
 def add_class(value, css_class):
     """
diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py
--- a/rest_framework/utils/serializer_helpers.py
+++ b/rest_framework/utils/serializer_helpers.py
@@ -78,7 +78,7 @@ def __repr__(self):
         ))
 
     def as_form_field(self):
-        value = '' if (self.value is None or self.value is False) else force_text(self.value)
+        value = '' if (self.value is None or self.value is False) else self.value
         return self.__class__(self._field, value, self.errors, self._prefix)
 
 


Test patch:
diff --git a/tests/test_renderers.py b/tests/test_renderers.py
--- a/tests/test_renderers.py
+++ b/tests/test_renderers.py
@@ -481,3 +481,90 @@ def test_render_with_provided_args(self):
         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('<option value="2" selected>Option2</option>',
+                          result)
+        self.assertInHTML('<option value="1">Option1</option>', result)
+        self.assertInHTML('<option value="12">Option12</option>', 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('<option value="12" selected>Option12</option>',
+                          result)
+        self.assertInHTML('<option value="1">Option1</option>', result)
+        self.assertInHTML('<option value="2">Option2</option>', 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('<option value="12" selected>Option12</option>',
+                          result)
+        self.assertInHTML('<option value="1">Option1</option>', result)
+        self.assertInHTML('<option value="2">Option2</option>', result)
+        self.assertInHTML('<option value="}">OptionBrace</option>', 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('<option value="12" selected>Option12</option>',
+                          result)
+        self.assertInHTML('<option value="1">Option1</option>', result)
+        self.assertInHTML('<option value="2">Option2</option>', result)