Improve field value handling in HTML form output

Let each form field's HTML template decide if it needs native or
string-ified field value, instead of forcing all templates to always
work only with unnecessarily restrictive string representation.

Fixes #4120, #4121
This commit is contained in:
Arek Korbik 2016-05-15 17:58:32 +02:00
parent 2cb71571cd
commit dc0442c186
12 changed files with 21 additions and 31 deletions

View File

@ -319,9 +319,6 @@ class HTMLFormRenderer(BaseRenderer):
style['template_pack'] = parent_style.get('template_pack', self.template_pack)
style['renderer'] = self
# Get a clone of the field with text-only value representation.
field = field.as_form_field()
if style.get('input_type') == 'datetime-local' and isinstance(field.value, six.text_type):
field.value = field.value.rstrip('Z')

View File

@ -6,7 +6,7 @@
{% endif %}
<div class="col-sm-10">
<input name="{{ field.name }}" {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.value %}value="{{ field.value }}"{% endif %}>
<input name="{{ field.name }}" {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.string_value %}value="{{ field.string_value }}"{% endif %}>
{% if field.errors %}
{% for error in field.errors %}

View File

@ -8,7 +8,7 @@
<div class="col-sm-10">
<select class="form-control" name="{{ field.name }}">
{% if field.allow_null or field.allow_blank %}
<option value="" {% if not field.value %}selected{% endif %}>--------</option>
<option value="" {% if not field.string_value %}selected{% endif %}>--------</option>
{% endif %}
{% for select in field.iter_options %}
{% if select.start_option_group %}

View File

@ -6,7 +6,7 @@
{% endif %}
<div class="col-sm-10">
<textarea name="{{ field.name }}" class="form-control" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if style.rows %}rows="{{ style.rows }}"{% endif %}>{% if field.value %}{{ field.value }}{% endif %}</textarea>
<textarea name="{{ field.name }}" class="form-control" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if style.rows %}rows="{{ style.rows }}"{% endif %}>{% if field.string_value %}{{ field.string_value }}{% endif %}</textarea>
{% if field.errors %}
{% for error in field.errors %}

View File

@ -5,5 +5,5 @@
</label>
{% endif %}
<input name="{{ field.name }}" {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.value %}value="{{ field.value }}"{% endif %}>
<input name="{{ field.name }}" {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.string_value %}value="{{ field.string_value }}"{% endif %}>
</div>

View File

@ -7,7 +7,7 @@
<select class="form-control" name="{{ field.name }}">
{% if field.allow_null or field.allow_blank %}
<option value="" {% if not field.value %}selected{% endif %}>--------</option>
<option value="" {% if not field.string_value %}selected{% endif %}>--------</option>
{% endif %}
{% for select in field.iter_options %}
{% if select.start_option_group %}

View File

@ -5,5 +5,5 @@
</label>
{% endif %}
<input name="{{ field.name }}" type="text" class="form-control" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.value %}value="{{ field.value }}"{% endif %}>
<input name="{{ field.name }}" type="text" class="form-control" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.string_value %}value="{{ field.string_value }}"{% endif %}>
</div>

View File

@ -3,7 +3,7 @@
<label {% if style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</label>
{% endif %}
<input name="{{ field.name }}" {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.value %}value="{{ field.value }}"{% endif %}>
<input name="{{ field.name }}" {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.string_value %}value="{{ field.string_value }}"{% endif %}>
{% if field.errors %}
{% for error in field.errors %}

View File

@ -7,7 +7,7 @@
<select class="form-control" name="{{ field.name }}">
{% if field.allow_null or field.allow_blank %}
<option value="" {% if not field.value %}selected{% endif %}>--------</option>
<option value="" {% if not field.string_value %}selected{% endif %}>--------</option>
{% endif %}
{% for select in field.iter_options %}
{% if select.start_option_group %}

View File

@ -5,7 +5,7 @@
</label>
{% endif %}
<textarea name="{{ field.name }}" class="form-control" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if style.rows %}rows="{{ style.rows }}"{% endif %}>{% if field.value %}{{ field.value }}{% endif %}</textarea>
<textarea name="{{ field.name }}" class="form-control" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if style.rows %}rows="{{ style.rows }}"{% endif %}>{% if field.string_value %}{{ field.string_value }}{% endif %}</textarea>
{% if field.errors %}
{% for error in field.errors %}<span class="help-block">{{ error }}</span>{% endfor %}

View File

@ -72,15 +72,17 @@ class BoundField(object):
def _proxy_class(self):
return self._field.__class__
@property
def string_value(self):
if self.value is None or self.value is False:
return ''
return force_text(self.value)
def __repr__(self):
return unicode_to_repr('<%s value=%s errors=%s>' % (
self.__class__.__name__, self.value, self.errors
))
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)
class NestedBoundField(BoundField):
"""
@ -106,15 +108,6 @@ class NestedBoundField(BoundField):
return NestedBoundField(field, value, error, prefix=self.name + '.')
return BoundField(field, value, error, prefix=self.name + '.')
def as_form_field(self):
values = {}
for key, value in self.value.items():
if isinstance(value, (list, dict)):
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)
class BindingDict(collections.MutableMapping):
"""

View File

@ -45,15 +45,15 @@ class TestSimpleBoundField:
assert serializer['amount'].errors is None
assert serializer['amount'].name == 'amount'
def test_as_form_fields(self):
def test_string_value(self):
class ExampleSerializer(serializers.Serializer):
bool_field = serializers.BooleanField()
null_field = serializers.IntegerField(allow_null=True)
serializer = ExampleSerializer(data={'bool_field': False, 'null_field': None})
assert serializer.is_valid()
assert serializer['bool_field'].as_form_field().value == ''
assert serializer['null_field'].as_form_field().value == ''
assert serializer['bool_field'].string_value == ''
assert serializer['null_field'].string_value == ''
class TestNestedBoundField:
@ -78,7 +78,7 @@ class TestNestedBoundField:
assert serializer['nested']['amount'].errors is None
assert serializer['nested']['amount'].name == 'nested.amount'
def test_as_form_fields(self):
def test_string_value(self):
class Nested(serializers.Serializer):
bool_field = serializers.BooleanField()
null_field = serializers.IntegerField(allow_null=True)
@ -88,8 +88,8 @@ class TestNestedBoundField:
serializer = ExampleSerializer(data={'nested': {'bool_field': False, 'null_field': None}})
assert serializer.is_valid()
assert serializer['nested']['bool_field'].as_form_field().value == ''
assert serializer['nested']['null_field'].as_form_field().value == ''
assert serializer['nested']['bool_field'].string_value == ''
assert serializer['nested']['null_field'].string_value == ''
def test_rendering_nested_fields_with_none_value(self):
from rest_framework.renderers import HTMLFormRenderer