Opened 20 months ago

Last modified 20 months ago

#33950 closed Bug

ModelChoiceField and chained prefetch_related(): queries made twice — at Version 2

Reported by: Vincent Lefoulon Owned by: nobody
Component: Uncategorized Version: 3.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Vincent Lefoulon)

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!

Change History (2)

comment:1 by Vincent Lefoulon, 20 months ago

Description: modified (diff)

comment:2 by Vincent Lefoulon, 20 months ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top