#31295: required ModelChoiceField makes duplicate (cursor) queries to the 
database
---------------------------------+--------------------------------------
     Reporter:  Aurélien Pardon  |                    Owner:  nobody
         Type:  Bug              |                   Status:  new
    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
---------------------------------+--------------------------------------
Changes (by Aurélien Pardon):

 * status:  closed => new
 * type:  Cleanup/optimization => Bug
 * resolution:  wontfix =>


Comment:

 Hi Carlton and thanks for your answer but, all due respect, I think you
 overlooked this bug.

 First, this simple example on an empty project/database that I just tested
 (Python 3.8.1, Django 2.2.10):
 {{{#!python
 class MyModel(models.Model):
     my_field = models.CharField(max_length=1)

 class MyForm(forms.Form):
     my_form_field = forms.ModelChoiceField(MyModel.objects.all(), ,
 empty_label=None)

 my_form = MyForm()
 type(my_form.widget.choices)
 # >>> return <class 'django.forms.models.ModelChoiceIterator'>

 my_form.as_ul()
 len(connecton.queries)
 # >>> return 2 (and it's two times "SELECT * from my_app.my_model")
 }}}

 Here is what I understand:
 1) The code you sent, where {{{ChoiceWidget.choices}}} is evaluated and
 saved into a {{{list}}} is executed at the initialization of the widget
 (which is executed at the initialization of the field at the declaration
 of the form).
 2) But! a {{{ModelChoiceField}}} override {{{self.choices}}}
 
[https://github.com/django/django/blob/271fdab8b78af558238df51c64b4d1c8dd0792bb/django/forms/models.py#L1225-L1227
 here] (it makes perfect sense: the choices have to be evaluated everytime
 the form is instanciated).
 3) As the choices are bundled in a
 {{{ModelChoiceIterator}}}/{{{.iterator()}}}, when rendering the form,
 there is a test about the {{{required}}} attribute that try to
 
[https://github.com/django/django/blob/ffcf1a8ebfbdc8e3afac84aed88d6ed29a57c72b/django/forms/widgets.py#L699
 fetch the first value of the queryset]:
 {{{#!python
 class Select(ChoiceWidget):
     """[...]"""
     def use_required_attribute(self, initial):
         """[...]"""
         first_choice = next(iter(self.choices), None)
 }}}
 4) {{{ModelChoiceIterator.__iter__}}}
 
[https://github.com/django/django/blob/ffcf1a8ebfbdc8e3afac84aed88d6ed29a57c72b/django/forms/models.py#L1148-L1156
 is called]. If {{{empty_label}}} is {{{not None}}}, everything is fine, as
 the {{{next()}}} grabs the empty label, without additional request to the
 database. But if {{{empty_label}}} is {{{None}}} (and if there is no
 prefetch to the queryset), an additional request is made (code is changed
 lightly to illustrate the bug):
 {{{#!python
     def __iter__(self):
         if self.field.empty_label is not None:
             yield ("", self.field.empty_label)
         for obj in self.queryset.iterator():
             yield self.choice(obj)
 }}}

 Now that I have made some tests on an empty project/database, I'm
 confident enough to state that it's a **bug** because there is a useless
 and additional request to the DB.

 ''Thanks for all the work you put into the projects in and around Django.
 I hope I tested correctly, I do not want to waste your time.''

-- 
Ticket URL: <https://code.djangoproject.com/ticket/31295#comment:3>
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 django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/065.40916b6b3e6158e3034b4cd5f350eca5%40djangoproject.com.

Reply via email to