#34791: Issue when using Prefetch objects in prefetch_related
-------------------------------------+-------------------------------------
Reporter: Maxime Toussaint | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: 4.2
(models, ORM) | Resolution:
Severity: Normal | worksforme
Keywords: prefetch, | Triage Stage:
prefetch_related | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Maxime Toussaint):
Hi, thanks a lot for the quick answer. I seem to have misunderstood the
parameters of my issue, and the example I gave does work for me. In trying
to simplify it, I took out the part that was making it fail.
So the issue only seems to happen when there is depth in the prefetch.
Here is an example I made this morning that fails:
{{{
pizzas = Pizza.objects.all().prefetch_related(
"toppings__origin",
Prefetch(
"toppings__origin",
queryset=Country.objects.filter(label="China"),
to_attr="china",
),
)
china = pizzas[0].toppings.all()[0].china
}}}
Here, when trying to get china, I would assume it to either be a Country
object or None. However, I get the message: AttributeError: 'Topping'
object has no attribute 'china'
Here are the models I set up for my test:
{{{
class Country(models.Model):
label = models.CharField(max_length=50)
class Pizza(models.Model):
label = models.CharField(max_length=50)
class Topping(models.Model):
pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE,
related_name="toppings")
label = models.CharField(max_length=50)
origin = models.ForeignKey(
Country, on_delete=models.CASCADE, related_name="toppings"
)
}}}
And here are the queries made when calling the queryset:
{{{
1. SELECT "tests_pizza"."id", "tests_pizza"."label",
"tests_pizza"."provenance_id" FROM "tests_pizza"
2. SELECT "tests_topping"."id", "tests_topping"."pizza_id",
"tests_topping"."label", "tests_topping"."origin_id" FROM "tests_topping"
WHERE "tests_topping"."pizza_id" IN (1, 2)
3. SELECT "tests_country"."id", "tests_country"."label",
"tests_country"."continent_id" FROM "tests_country" WHERE
"tests_country"."id" IN (2, 3, 4)
}}}
Note that the filter by label='china' has completely disappeared.
Now, if I switch the prefetches around like so:
{{{
pizzas = Pizza.objects.all().prefetch_related(
Prefetch(
"toppings__origin",
queryset=Country.objects.filter(label="China"),
to_attr="china",
),
"toppings__origin",
)
}}}
Fetching china now works, and here are the queries being made:
{{{
1. SELECT "tests_pizza"."id", "tests_pizza"."label",
"tests_pizza"."provenance_id" FROM "tests_pizza"
2. SELECT "tests_topping"."id", "tests_topping"."pizza_id",
"tests_topping"."label", "tests_topping"."origin_id" FROM "tests_topping"
WHERE "tests_topping"."pizza_id" IN (1, 2)
3. SELECT "tests_country"."id", "tests_country"."label",
"tests_country"."continent_id" FROM "tests_country" WHERE
("tests_country"."label" = 'China' AND "tests_country"."id" IN (2, 3, 4))
4. SELECT "tests_country"."id", "tests_country"."label",
"tests_country"."continent_id" FROM "tests_country" WHERE
"tests_country"."id" IN (2, 3, 4)
}}}
This time, both calls to Country were made.
I did follow the code a bit yesterday, and I believe it comes from the
fact that the ForeignKey field defines the cache name as being simply the
name of the field. I am not certain though, and it would likely require
someone with more knowledge than me to look into it.
Thanks again, hopefully we can figure out what is happening!
--
Ticket URL: <https://code.djangoproject.com/ticket/34791#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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018a22777992-fa2247c0-3887-4d1b-a631-8d73f04bb5f6-000000%40eu-central-1.amazonses.com.