Loading django/forms/forms.py +17 −7 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ def pretty_name(name): return '' return name.replace('_', ' ').capitalize() UNSET = object() class DeclarativeFieldsMetaclass(MediaDefiningClass): """ Loading Loading @@ -93,6 +95,7 @@ class BaseForm(object): # Instances should always modify self.fields; they should not modify # self.base_fields. self.fields = copy.deepcopy(self.base_fields) self._bound_fields_cache = {} def __str__(self): return self.as_table() Loading Loading @@ -120,7 +123,9 @@ class BaseForm(object): except KeyError: raise KeyError( "Key %r not found in '%s'" % (name, self.__class__.__name__)) return BoundField(self, field, name) if name not in self._bound_fields_cache: self._bound_fields_cache[name] = BoundField(self, field, name) return self._bound_fields_cache[name] @property def errors(self): Loading Loading @@ -486,6 +491,7 @@ class BoundField(object): else: self.label = self.field.label self.help_text = field.help_text or '' self._initial_value = UNSET def __str__(self): """Renders this field as an HTML widget.""" Loading Loading @@ -580,12 +586,16 @@ class BoundField(object): if not self.form.is_bound: data = self.form.initial.get(self.name, self.field.initial) if callable(data): if self._initial_value is not UNSET: data = self._initial_value else: data = data() # If this is an auto-generated default date, nix the # microseconds for standardized handling. See #22502. if (isinstance(data, (datetime.datetime, datetime.time)) and not getattr(self.field.widget, 'supports_microseconds', True)): data = data.replace(microsecond=0) self._initial_value = data else: data = self.field.bound_data( self.data, self.form.initial.get(self.name, self.field.initial) Loading tests/forms_tests/tests/test_forms.py +15 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ from __future__ import unicode_literals import copy import datetime import json import uuid from django.core.exceptions import NON_FIELD_ERRORS from django.core.files.uploadedfile import SimpleUploadedFile Loading Loading @@ -1369,6 +1370,20 @@ class FormsTestCase(TestCase): self.assertEqual(bound['password'].value(), 'foo') self.assertEqual(unbound['password'].value(), None) def test_boundfield_initial_called_once(self): """ Multiple calls to BoundField().value() in an unbound form should return the same result each time (#24391). """ class MyForm(Form): name = CharField(max_length=10, initial=uuid.uuid4) form = MyForm() name = form['name'] self.assertEqual(name.value(), name.value()) # BoundField is also cached self.assertIs(form['name'], name) def test_boundfield_rendering(self): """ Python 2 issue: Test that rendering a BoundField with bytestring content Loading Loading
django/forms/forms.py +17 −7 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ def pretty_name(name): return '' return name.replace('_', ' ').capitalize() UNSET = object() class DeclarativeFieldsMetaclass(MediaDefiningClass): """ Loading Loading @@ -93,6 +95,7 @@ class BaseForm(object): # Instances should always modify self.fields; they should not modify # self.base_fields. self.fields = copy.deepcopy(self.base_fields) self._bound_fields_cache = {} def __str__(self): return self.as_table() Loading Loading @@ -120,7 +123,9 @@ class BaseForm(object): except KeyError: raise KeyError( "Key %r not found in '%s'" % (name, self.__class__.__name__)) return BoundField(self, field, name) if name not in self._bound_fields_cache: self._bound_fields_cache[name] = BoundField(self, field, name) return self._bound_fields_cache[name] @property def errors(self): Loading Loading @@ -486,6 +491,7 @@ class BoundField(object): else: self.label = self.field.label self.help_text = field.help_text or '' self._initial_value = UNSET def __str__(self): """Renders this field as an HTML widget.""" Loading Loading @@ -580,12 +586,16 @@ class BoundField(object): if not self.form.is_bound: data = self.form.initial.get(self.name, self.field.initial) if callable(data): if self._initial_value is not UNSET: data = self._initial_value else: data = data() # If this is an auto-generated default date, nix the # microseconds for standardized handling. See #22502. if (isinstance(data, (datetime.datetime, datetime.time)) and not getattr(self.field.widget, 'supports_microseconds', True)): data = data.replace(microsecond=0) self._initial_value = data else: data = self.field.bound_data( self.data, self.form.initial.get(self.name, self.field.initial) Loading
tests/forms_tests/tests/test_forms.py +15 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ from __future__ import unicode_literals import copy import datetime import json import uuid from django.core.exceptions import NON_FIELD_ERRORS from django.core.files.uploadedfile import SimpleUploadedFile Loading Loading @@ -1369,6 +1370,20 @@ class FormsTestCase(TestCase): self.assertEqual(bound['password'].value(), 'foo') self.assertEqual(unbound['password'].value(), None) def test_boundfield_initial_called_once(self): """ Multiple calls to BoundField().value() in an unbound form should return the same result each time (#24391). """ class MyForm(Form): name = CharField(max_length=10, initial=uuid.uuid4) form = MyForm() name = form['name'] self.assertEqual(name.value(), name.value()) # BoundField is also cached self.assertIs(form['name'], name) def test_boundfield_rendering(self): """ Python 2 issue: Test that rendering a BoundField with bytestring content Loading