mirror of https://github.com/django/django.git
Fixed #27002 -- Prevented double query when rendering ModelChoiceField.
This commit is contained in:
parent
29a3f8b4bb
commit
74105b2636
|
@ -8,6 +8,7 @@ from django.utils import six
|
||||||
from django.utils.encoding import (
|
from django.utils.encoding import (
|
||||||
force_text, python_2_unicode_compatible, smart_text,
|
force_text, python_2_unicode_compatible, smart_text,
|
||||||
)
|
)
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from django.utils.html import conditional_escape, format_html, html_safe
|
from django.utils.html import conditional_escape, format_html, html_safe
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -42,28 +43,32 @@ class BoundField(object):
|
||||||
return self.as_widget() + self.as_hidden(only_initial=True)
|
return self.as_widget() + self.as_hidden(only_initial=True)
|
||||||
return self.as_widget()
|
return self.as_widget()
|
||||||
|
|
||||||
def __iter__(self):
|
@cached_property
|
||||||
|
def subwidgets(self):
|
||||||
"""
|
"""
|
||||||
Yields rendered strings that comprise all widgets in this BoundField.
|
Most widgets yield a single subwidget, but others like RadioSelect and
|
||||||
|
CheckboxSelectMultiple produce one subwidget for each choice.
|
||||||
|
|
||||||
This really is only useful for RadioSelect widgets, so that you can
|
This property is cached so that only one database query occurs when
|
||||||
iterate over individual radio buttons in a template.
|
rendering ModelChoiceFields.
|
||||||
"""
|
"""
|
||||||
id_ = self.field.widget.attrs.get('id') or self.auto_id
|
id_ = self.field.widget.attrs.get('id') or self.auto_id
|
||||||
attrs = {'id': id_} if id_ else {}
|
attrs = {'id': id_} if id_ else {}
|
||||||
attrs = self.build_widget_attrs(attrs)
|
attrs = self.build_widget_attrs(attrs)
|
||||||
for subwidget in self.field.widget.subwidgets(self.html_name, self.value(), attrs):
|
return list(self.field.widget.subwidgets(self.html_name, self.value(), attrs))
|
||||||
yield subwidget
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.subwidgets)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(list(self.__iter__()))
|
return len(self.subwidgets)
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
# Prevent unnecessary reevaluation when accessing BoundField's attrs
|
# Prevent unnecessary reevaluation when accessing BoundField's attrs
|
||||||
# from templates.
|
# from templates.
|
||||||
if not isinstance(idx, six.integer_types + (slice,)):
|
if not isinstance(idx, six.integer_types + (slice,)):
|
||||||
raise TypeError
|
raise TypeError
|
||||||
return list(self.__iter__())[idx]
|
return self.subwidgets[idx]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def errors(self):
|
def errors(self):
|
||||||
|
|
|
@ -1576,11 +1576,22 @@ class ModelChoiceFieldTests(TestCase):
|
||||||
field = CustomModelChoiceField(Category.objects.all())
|
field = CustomModelChoiceField(Category.objects.all())
|
||||||
self.assertIsInstance(field.choices, CustomModelChoiceIterator)
|
self.assertIsInstance(field.choices, CustomModelChoiceIterator)
|
||||||
|
|
||||||
def test_radioselect_num_queries(self):
|
def test_modelchoicefield_num_queries(self):
|
||||||
class CategoriesForm(forms.Form):
|
"""
|
||||||
categories = forms.ModelChoiceField(Category.objects.all(), widget=forms.RadioSelect)
|
Widgets that render multiple subwidgets shouldn't make more than one
|
||||||
|
database query.
|
||||||
|
"""
|
||||||
|
categories = Category.objects.all()
|
||||||
|
|
||||||
|
class CategoriesForm(forms.Form):
|
||||||
|
radio = forms.ModelChoiceField(queryset=categories, widget=forms.RadioSelect)
|
||||||
|
checkbox = forms.ModelMultipleChoiceField(queryset=categories, widget=forms.CheckboxSelectMultiple)
|
||||||
|
|
||||||
|
template = Template("""
|
||||||
|
{% for widget in form.checkbox %}{{ widget }}{% endfor %}
|
||||||
|
{% for widget in form.radio %}{{ widget }}{% endfor %}
|
||||||
|
""")
|
||||||
|
|
||||||
template = Template('{% for widget in form.categories %}{{ widget }}{% endfor %}')
|
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(2):
|
||||||
template.render(Context({'form': CategoriesForm()}))
|
template.render(Context({'form': CategoriesForm()}))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue