#33950: ModelChoiceField and chained prefetch_related(): queries made twice
----------------------------------+--------------------------------------
     Reporter:  Vincent Lefoulon  |                    Owner:  nobody
         Type:  Bug               |                   Status:  new
    Component:  Uncategorized     |                  Version:  3.2
     Severity:  Normal            |               Resolution:
     Keywords:                    |             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 Vincent Lefoulon:

Old description:

> Hi!
>
> I have these three models:
>
> {{{
> class Place(models.Model):
>     name = models.CharField(max_length=255)
>
> class Visit(models.Model):
>     date = models.DateField()
>     place = models.ForeignKey(Place, on_delete=models.CASCADE,
> related_name="visits")
>
> class VisitDocument(models.Model):
>     visit = models.ForeignKey(Visit, on_delete=models.CASCADE,
> related_name="documents")
>     file = models.FileField()
> }}}
>
> A form to edit a visit:
>
> {{{
> class VisitForm(forms.ModelForm):
>     class Meta:
>         model = Visit
>         exclude = ("place",)
>
>     def __init__(self, *args, **kwargs):
>         super().__init__(*args, **kwargs)
>
>         self.fields["documents_to_delete"] =
> forms.ModelMultipleChoiceField(
>             queryset=self.instance.documents.all(),
>             required=False,
>         )
> }}}
>
> And a generic view for the places:
>
> {{{
> class PlaceView(DetailView):
>     queryset = Place.objects.prefetch_related("visits__documents")
>
>     def get_context_data(self, **kwargs):
>         context = super().get_context_data(**kwargs)
>         if "visit_form" not in context:
>             context["visit_forms"] = [
>                 VisitForm(instance=visit)
>                 for visit in self.object.visits.all()
>             ]
>
>     def post(self, request, *args, **kwargs):
>         // Handle the form
> }}}
>
> Despite the {{{prefetch_related("visits__documents")}}} call in my view,
> {{{ModelMultipleChoiceField()}}} doesn't detect that the documents of the
> visit (i.e. the form instance) are already fetched because
> {{{visit.documents.all()._prefetch_related_lookups}}} is null:
> https://github.com/django/django/blob/stable/3.2.x/django/forms/models.py#L1167
>
> So is in the view:
> {{{self.object.visits.all()._prefetch_related_lookups}}} is null as well.
>
> Many thanks!

New description:

 Hi!

 I have these three models:

 {{{
 class Place(models.Model):
     name = models.CharField(max_length=255)

 class Visit(models.Model):
     date = models.DateField()
     place = models.ForeignKey(Place, on_delete=models.CASCADE,
 related_name="visits")

 class VisitDocument(models.Model):
     visit = models.ForeignKey(Visit, on_delete=models.CASCADE,
 related_name="documents")
     file = models.FileField()
 }}}

 A form to edit a visit:

 {{{
 class VisitForm(forms.ModelForm):
     class Meta:
         model = Visit
         exclude = ("place",)

     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)

         self.fields["documents_to_delete"] =
 forms.ModelMultipleChoiceField(
             queryset=self.instance.documents.all(),
             required=False,
         )
 }}}

 And a generic view for the places:

 {{{
 class PlaceView(DetailView):
     queryset = Place.objects.prefetch_related("visits__documents")

     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         if "visit_forms" not in context:
             context["visit_forms"] = [
                 VisitForm(instance=visit)
                 for visit in self.object.visits.all()
             ]

     def post(self, request, *args, **kwargs):
         // Handle the form
 }}}

 Despite the {{{prefetch_related("visits__documents")}}} call in my view,
 {{{ModelMultipleChoiceField()}}} doesn't detect that the documents of the
 visit (i.e. the form instance) are already fetched because
 {{{visit.documents.all()._prefetch_related_lookups}}} is null:
 https://github.com/django/django/blob/stable/3.2.x/django/forms/models.py#L1167

 So is in the view:
 {{{self.object.visits.all()._prefetch_related_lookups}}} is null as well.

 Then the queries are made twice, erasing the {{{prefetch_related()}}}
 effect.

 Many thanks!

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/33950#comment:2>
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/01070182ca2e9a00-ab9ef0d8-815d-45a7-8fa9-2aac2d514aa8-000000%40eu-central-1.amazonses.com.

Reply via email to