Opened 51 minutes ago

Last modified 37 minutes ago

#37267 new Bug

Prefetch can populate a reverse foreign key cache with instances of an unrelated queryset model

Reported by: beingfaisal Owned by:
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: Prefetch prefetch_related queryset model
Cc: beingfaisal Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Prefetch() accepts a custom queryset whose model is unrelated to the
model represented by a reverse foreign key lookup. If both models contain
a foreign key with the same name, Django can populate the relationship
cache with instances of the wrong model.

For example:

class Patient(models.Model):
    pass


class ChatAlert(models.Model):
    patient = models.ForeignKey(
        Patient,
        related_name="chatalerts",
        on_delete=models.CASCADE,
    )


class HealthAlert(models.Model):
    patient = models.ForeignKey(
        Patient,
        related_name="healthalerts",
        on_delete=models.CASCADE,
    )

The following prefetch is accepted:

patient = Patient.objects.create()
health_alert = HealthAlert.objects.create(patient=patient)

patient = Patient.objects.prefetch_related(
    Prefetch(
        "chatalerts",
        queryset=HealthAlert.objects.all(),
    )
).get(pk=patient.pk)

alerts = list(patient.chatalerts.all())

Patient.chatalerts represents the reverse side of
ChatAlert.patient, so it should only contain ChatAlert instances.
Instead, alerts contains the HealthAlert instance.

Both models expose patient and patient_id, so the reverse foreign key
prefetch machinery can filter and group the incompatible queryset without
raising an error. With a different schema, the same invalid configuration
may instead fail later with a less clear field or attribute error.

I expect evaluating the prefetch to raise ValueError when the custom
queryset model is incompatible with the relationship model.

A regression test using existing prefetch_related test models fails with:

AssertionError: ValueError not raised

Custom querysets using subclasses of the expected relationship model must
remain supported, as established by #36432. Therefore, compatibility should
be directional: the supplied queryset model may be the expected model or
one of its subclasses, but not an unrelated model.

AI assistance: OpenAI Codex (GPT-5) helped analyze and draft this report.
I manually verified the reproduction.

Change History (1)

comment:1 by beingfaisal, 37 minutes ago

For completeness, the issue can be reproduced using models already present
in Django's test suite. The following test can be added to
PrefetchRelatedTests in tests/prefetch_related/tests.py:

def test_foreignkey_reverse_incompatible_queryset_model(self):
    FavoriteAuthors.objects.create(
        author=self.author1,
        likes_author=self.author2,
    )
    queryset = Author.objects.prefetch_related(
        Prefetch(
            "addresses",
            queryset=FavoriteAuthors.objects.all(),
        )
    )

    with self.assertRaises(ValueError):
        list(queryset)

Author.addresses represents the reverse side of
AuthorAddress.author, so it expects AuthorAddress instances. The
custom queryset instead returns unrelated FavoriteAuthors instances.

Both models expose author and author_id, so current main evaluates
the prefetch without raising an exception. The test therefore fails with:

AssertionError: ValueError not raised
Note: See TracTickets for help on using tickets.
Back to Top