#28101: Wrong field used for sub query lookup on nested query using to_field
ForeignKey
-------------------------------------+-------------------------------------
               Reporter:  klette     |          Owner:  nobody
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  1.11
  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          |
-------------------------------------+-------------------------------------
 Given the following models:

 {{{#!python
 class CustomerUser(models.Model):
     name = models.CharField(max_length=100)


 class Customer(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4,
 editable=False)
     customer_number = models.CharField(unique=True, max_length=100)
     users = models.ManyToManyField(CustomerUser)


 class CustomerProduct(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4,
 editable=False)
     customer = models.ForeignKey(
         Customer, to_field='customer_number', on_delete=models.PROTECT)
     name = models.CharField(max_length=100)


 class CustomerPayment(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4,
 editable=False)
     product = models.ForeignKey(CustomerProduct, on_delete=models.PROTECT)
 }}}

 And the tests:

 {{{#!python
 class SubQueryM2MWithToFieldFkTests(TestCase):
     def test_regression(self):
         user = CustomerUser.objects.create(name='my user')
         customer = Customer.objects.create(customer_number='A123')
         customer.users.add(user)
         product = CustomerProduct.objects.create(customer=customer,
 name='Foo')
         payment = CustomerPayment.objects.create(product=product)

         products = CustomerProduct.objects.filter(
             customer__in=user.customer_set.all())

         result = CustomerPayment.objects.filter(product__in=products)
         self.assertEquals(result.count(), 1)
         self.assertEquals(list(result), [payment])
 }}}

 One should get the query:
 {{{#!sql
 SELECT "queries_customerpayment"."id",
 "queries_customerpayment"."product_id"
 FROM "queries_customerpayment"
 WHERE "queries_customerpayment"."product_id" IN (
   SELECT V0."id" AS Col1 FROM "queries_customerproduct" V0
   WHERE V0."customer_id" IN (
     SELECT U0."customer_number" AS Col1
     FROM "queries_customer" U0 INNER JOIN "queries_customer_users" U1 ON
 (U0."id" = U1."customer_id")
     WHERE U1."customeruser_id" = 1))
 }}}

 But at least 1.11 and master generates:

 {{{#!sql
 SELECT "queries_customerpayment"."id",
 "queries_customerpayment"."product_id"
 FROM "queries_customerpayment"
 WHERE "queries_customerpayment"."product_id" IN (
    SELECT V0."id" AS Col1 FROM "queries_customerproduct" V0
    WHERE V0."customer_id" IN (
       SELECT U0."id" AS Col1 FROM "queries_customer" U0 INNER JOIN
 "queries_customer_users" U1 ON (U0."id" = U1."customer_id")
       WHERE U1."customeruser_id" = 1))
 }}}

 which attempts to match the wrong `Customer`-field to
 `CustomerProduct.customer_id`.

 I tried to figure out what was going on and the test passes if I add
 `print(self)` to `Queryset._prepare_as_filter_value` in the forced pk
 block. Quite arbitrary placement of the print, and probably not related to
 `_prepare_as_filter_value`. My guess would be that the evaluation of the
 inner queryset fixes some field matching some other place - but I'm way
 out of my depth here :)

 The tests were added directly to `tests/queries/tests.py` and the models
 to `tests/queries/models.py` and run using Django's test suite.

 Might be related: [https://code.djangoproject.com/ticket/26196]

--
Ticket URL: <https://code.djangoproject.com/ticket/28101>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/049.b6f28a8d37a3a7c21f5eba5c97999b26%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to