#31295: required ModelChoiceField makes duplicate (cursor) queries to the
database
-------------------------------------+-------------------------------------
Reporter: Aurélien Pardon | Owner: nobody
Type: | Status: new
Cleanup/optimization |
Component: Forms | Version: 2.2
Severity: Normal | Resolution:
Keywords: Model | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Aurélien Pardon:
Old description:
> ModelChoiceField use ModelChoiceIterator for its {{{ queryset }}}/{{{
> self.choices }}}, which use {{{.iterator()}}} and doesn't cache the query
> under some conditions.
>
> If the field is required, the method use_required_attribute
> (https://github.com/django/django/blob/master/django/forms/widgets.py#L689)
> fetch the first choice, making a duplicate query to the database (worse
> than a useless query, the data may have changed):
> {{{#!python
> class Select(ChoiceWidget):
> [...]
>
> def use_required_attribute(self, initial):
> [...]
> first_choice = next(iter(self.choices), None)
> }}}
>
> Disabling the use of {{{.iterator()}}} (by adding an arbitrary
> {{{.prefetch_related}}} for example) leads to no duplicate queries.
> https://github.com/django/django/blob/da79ee472d803963dc3ea81ee67767dc06068aac/django/forms/models.py#L1152
> :
> {{{#!python
> class ModelChoiceIterator:
> [...]
>
> def __iter__(self):
> [...]
> # Can't use iterator() when queryset uses prefetch_related()
> if not queryset._prefetch_related_lookups:
> queryset = queryset.iterator()
> }}}
>
> One solution would be to add another test to the previous piece of code :
> (https://github.com/django/django/blob/da79ee472d803963dc3ea81ee67767dc06068aac/django/forms/models.py#L1152)
> :
> {{{#!python
> if not queryset._prefetch_related_lookups and not
> self.field.required:
> queryset = queryset.iterator()
> }}}
New description:
ModelChoiceField use ModelChoiceIterator for its
{{{queryset}}}/{{{self.choices}}}, which use {{{.iterator()}}} and doesn't
cache the query under some conditions.
If the field is required, the method use_required_attribute
(https://github.com/django/django/blob/master/django/forms/widgets.py#L689)
fetch the first choice, making a duplicate query to the database (worse
than a useless query, the data may have changed):
{{{#!python
class Select(ChoiceWidget):
[...]
def use_required_attribute(self, initial):
[...]
first_choice = next(iter(self.choices), None)
}}}
Disabling the use of {{{.iterator()}}} (by adding an arbitrary
{{{.prefetch_related}}} for example) leads to no duplicate queries.
https://github.com/django/django/blob/da79ee472d803963dc3ea81ee67767dc06068aac/django/forms/models.py#L1152
:
{{{#!python
class ModelChoiceIterator:
[...]
def __iter__(self):
[...]
# Can't use iterator() when queryset uses prefetch_related()
if not queryset._prefetch_related_lookups:
queryset = queryset.iterator()
}}}
One solution would be to add another test to the previous piece of code :
{{{#!python
if not queryset._prefetch_related_lookups and not
self.field.required:
queryset = queryset.iterator()
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/31295#comment:1>
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 view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/065.aec7d366728d2cb6c76ff74faf52c862%40djangoproject.com.