#27002: Redundant database query rendering a ModelChoiceField with RadioSelect 
or
CheckboxSelectMultiple
----------------------------+--------------------
     Reporter:  AlexHill    |      Owner:  nobody
         Type:  Bug         |     Status:  new
    Component:  Forms       |    Version:  1.10
     Severity:  Normal      |   Keywords:
 Triage Stage:  Unreviewed  |  Has patch:  0
Easy pickings:  0           |      UI/UX:  0
----------------------------+--------------------
 Followup to #27001:

 Rendering a ModelChoiceField with RadioSelect and CheckboxSelectMultiple,
 there's a redundant iteration that triggers an unnecessary database call.

 Here's the relevant code from ForNode.render
 
([https://github.com/django/django/blob/ccd5a23fba91e66656bb19b60bb6d737cab728f5/django/template/defaulttags.py#L164
 GitHub link]):

 {{{#!python
             if not hasattr(values, '__len__'):  # line 164
                 values = list(values)
             len_values = len(values)
             ...
             for i, item in enumerate(values):  # line 177
                 ...
 }}}

 In this code, `values` is a BoundField. BoundField defines \__len__ like
 this:

 {{{#!python
     def __len__(self):
         return len(list(self.__iter__()))
 }}}

 So `len(values)` on line 164 triggers an iteration over `values` (and thus
 a database query), then the for loop at line 177 triggers another.

 The options as I see them are
 1. Remove `BoundField.__len__` so that `list(values)` is called in
 `ForNode.render`. This fixes the problem without breaking any tests. It is
 backwards-incompatible (e.g. with an existing template passing a
 boundfield to the `length` template filter), but not with anything
 documented.
 2. Cache iterator results somewhere internally so that the actual query is
 only executed once. This is easy to do on BoundField with a cached
 property. No backward compatibility concerns here.
 3. Unconditionally call `list(values)` in ForNode. Also works, but a bit
 heavy-handed and not necessary if one of the first two options are
 acceptable.

 I think (2) is the best choice. It's pretty clean, and it means the fix
 can be implemented in 1.10 (and 1.8 LTS?) as a bug fix without backward
 compatibility concerns.

--
Ticket URL: <https://code.djangoproject.com/ticket/27002>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/051.e33d244f20020c2d5fee6df85ec740f5%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to