#35376: Prefetched data not used when combining prefetch_related() and only()
-------------------------------------+-------------------------------------
Reporter: Michael | Owner: nobody
Schwarz |
Type: | Status: new
Uncategorized |
Component: Database | Version: 4.2
layer (models, ORM) |
Severity: Normal | Keywords:
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
I think I found a simple case combining `prefetch_related()` and `only()`,
where prefetched data isn't used when it should.
See the following model with a `Restaurant` being a `Building`, and a
`Building` being part of a `City`. (replace `Building` with `Business` if
it bugs you that a building can only contain a single restaurant 🙃, I
noticed too late):
{{{#!python
from django.db.models import DO_NOTHING
from django.db.models import ForeignKey
from django.db.models import Model
from django.db.models import OneToOneField
from django.db.models import TextField
class City(Model):
name = TextField()
class Building(Model):
city = ForeignKey(City, on_delete=DO_NOTHING)
street_name = TextField()
street_no = TextField()
class Restaurant(Model):
building = OneToOneField(Building, on_delete=DO_NOTHING)
name = TextField()
}}}
I'm trying to build a query set of buildings with some of its attributes
deferred using `only()`, and the associated restaurants and cities being
prefetched. In the following parametrized `pytest` test case, only the
last instance is exhibiting the issue, the other 3 seem to work as I would
expect.
The test case creates an instance of each model, runs the query
(`list(...)`), and then accesses the `restaurant` attribute, which should
be prefetched in every case. The test case check that the access does not
generate an additional query using `django_assert_num_queries()` from
`pytest-django`.
{{{#!python
import pytest
from myproject.models import Building
from myproject.models import Restaurant
from myproject.models import City
@pytest.mark.parametrize('qs', [
Building.objects.prefetch_related("restaurant", "city"),
Building.objects.only("street_name").prefetch_related("restaurant"),
Building.objects.only("street_name").prefetch_related("city",
"restaurant"),
Building.objects.only("street_name").prefetch_related("restaurant",
"city")
])
def test_repro(db, django_assert_num_queries, qs):
Restaurant.objects.create(
name="",
building=Building.objects.create(
city=City.objects.create(name=""), street_name="",
street_no=""
),
)
result = list(qs)
with django_assert_num_queries(0):
result[0].restaurant
}}}
The first 3 instances of the test above succeed, the last one fails:
{{{
$ venv/bin/pytest test_repro.py
[...]
@pytest.mark.parametrize('qs', [
Building.objects.prefetch_related("restaurant", "city"),
Building.objects.only("street_name").prefetch_related("restaurant"),
Building.objects.only("street_name").prefetch_related("city",
"restaurant"),
Building.objects.only("street_name").prefetch_related("restaurant",
"city")
])
def test_repro(db, django_assert_num_queries, qs):
Restaurant.objects.create(
name="",
building=Building.objects.create(
city=City.objects.create(name=""), street_name="",
street_no=""
),
)
result = list(qs)
> with django_assert_num_queries(0):
[...]
E Failed: Expected to perform 0 queries but 1 was done (add
-v option to show queries)
}}}
I've created a [https://github.com/Feuermurmel/only_prefetch_related_repro
minimal project on GitHub] documenting the exact setup, including all
package versions.
AFAICT, in each case above, `restaurant` should be prefetched, and the
attribute access should not generate an additional access. Only when
`only()` is used, another related model is also prefetched, and that other
model is mentioned _after_ `restaurant` in the call to
`prefetch_related()`, the prefetched data for `restaurant` isn't used.
Running macOS 12.7.4, Python 3.12.2 and Django 4.2.11.
--
Ticket URL: <https://code.djangoproject.com/ticket/35376>
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/0107018ee21b3e85-1d87db03-6000-42ba-8487-fd687c8a7b9a-000000%40eu-central-1.amazonses.com.