#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.

Reply via email to