Opened 5 years ago
Last modified 5 years ago
#31295 new Cleanup/optimization
required ModelChoiceField makes duplicate (cursor) queries to the database — at Version 1
Reported by: | Aurélien Pardon | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 2.2 |
Severity: | Normal | Keywords: | Model |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
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):
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 :
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 :
if not queryset._prefetch_related_lookups and not self.field.required: queryset = queryset.iterator()