Opened 6 years ago
Last modified 6 years ago
#31295 new Cleanup/optimization
required ModelChoiceField makes duplicate (cursor) queries to the database — at Initial Version
| 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
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 :
(https://github.com/django/django/blob/da79ee472d803963dc3ea81ee67767dc06068aac/django/forms/models.py#L1152) :
if not queryset._prefetch_related_lookups and not self.field.required: queryset = queryset.iterator()