Opened 4 years ago

Last modified 4 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()

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top