Fixed #24391 -- Made BoundField.value() cache callable values.
This commit is contained in:
parent
d298b1ba50
commit
65441bbdb0
|
@ -29,6 +29,8 @@ def pretty_name(name):
|
||||||
return ''
|
return ''
|
||||||
return name.replace('_', ' ').capitalize()
|
return name.replace('_', ' ').capitalize()
|
||||||
|
|
||||||
|
UNSET = object()
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeFieldsMetaclass(MediaDefiningClass):
|
class DeclarativeFieldsMetaclass(MediaDefiningClass):
|
||||||
"""
|
"""
|
||||||
|
@ -93,6 +95,7 @@ class BaseForm(object):
|
||||||
# Instances should always modify self.fields; they should not modify
|
# Instances should always modify self.fields; they should not modify
|
||||||
# self.base_fields.
|
# self.base_fields.
|
||||||
self.fields = copy.deepcopy(self.base_fields)
|
self.fields = copy.deepcopy(self.base_fields)
|
||||||
|
self._bound_fields_cache = {}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.as_table()
|
return self.as_table()
|
||||||
|
@ -120,7 +123,9 @@ class BaseForm(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
"Key %r not found in '%s'" % (name, self.__class__.__name__))
|
"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
|
@property
|
||||||
def errors(self):
|
def errors(self):
|
||||||
|
@ -486,6 +491,7 @@ class BoundField(object):
|
||||||
else:
|
else:
|
||||||
self.label = self.field.label
|
self.label = self.field.label
|
||||||
self.help_text = field.help_text or ''
|
self.help_text = field.help_text or ''
|
||||||
|
self._initial_value = UNSET
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Renders this field as an HTML widget."""
|
"""Renders this field as an HTML widget."""
|
||||||
|
@ -580,12 +586,16 @@ class BoundField(object):
|
||||||
if not self.form.is_bound:
|
if not self.form.is_bound:
|
||||||
data = self.form.initial.get(self.name, self.field.initial)
|
data = self.form.initial.get(self.name, self.field.initial)
|
||||||
if callable(data):
|
if callable(data):
|
||||||
|
if self._initial_value is not UNSET:
|
||||||
|
data = self._initial_value
|
||||||
|
else:
|
||||||
data = data()
|
data = data()
|
||||||
# If this is an auto-generated default date, nix the
|
# If this is an auto-generated default date, nix the
|
||||||
# microseconds for standardized handling. See #22502.
|
# microseconds for standardized handling. See #22502.
|
||||||
if (isinstance(data, (datetime.datetime, datetime.time)) and
|
if (isinstance(data, (datetime.datetime, datetime.time)) and
|
||||||
not getattr(self.field.widget, 'supports_microseconds', True)):
|
not getattr(self.field.widget, 'supports_microseconds', True)):
|
||||||
data = data.replace(microsecond=0)
|
data = data.replace(microsecond=0)
|
||||||
|
self._initial_value = data
|
||||||
else:
|
else:
|
||||||
data = self.field.bound_data(
|
data = self.field.bound_data(
|
||||||
self.data, self.form.initial.get(self.name, self.field.initial)
|
self.data, self.form.initial.get(self.name, self.field.initial)
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
from django.core.exceptions import NON_FIELD_ERRORS
|
from django.core.exceptions import NON_FIELD_ERRORS
|
||||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
|
@ -1369,6 +1370,20 @@ class FormsTestCase(TestCase):
|
||||||
self.assertEqual(bound['password'].value(), 'foo')
|
self.assertEqual(bound['password'].value(), 'foo')
|
||||||
self.assertEqual(unbound['password'].value(), None)
|
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):
|
def test_boundfield_rendering(self):
|
||||||
"""
|
"""
|
||||||
Python 2 issue: Test that rendering a BoundField with bytestring content
|
Python 2 issue: Test that rendering a BoundField with bytestring content
|
||||||
|
|
Loading…
Reference in New Issue