#26515: trim_joins bug with nested related models using ForeignObject -------------------------------------+------------------------------------- Reporter: ornoone | Owner: nobody Type: Bug | Status: new Component: Database layer | Version: 1.9 (models, ORM) | Keywords: ForeignObject, Severity: Normal | trim_join, orm Triage Stage: Unreviewed | Has patch: 1 Easy pickings: 0 | UI/UX: 0 -------------------------------------+------------------------------------- the order in which we declare the `from_fields` and `to_fields` does matter if we make a nested lookup.
it is clear that the fields in from_fields and to_fields must be on the same orders, but if 3 models use ForeignObject to links themselves, and the fields are the sames on all 3 models, the order in which they are declared in both fields must be the same. a example app that can trigger the bug follow : {{{#!python class Address(models.Model): company = models.CharField(max_length=1) tiers_id = models.IntegerField() city = models.CharField(max_length=255) postcode = models.CharField(max_length=32) class Meta(object): unique_together = [ ("company", "tiers_id"), ] class Customer(models.Model): company = models.CharField(max_length=1) customer_id = models.IntegerField() name = models.CharField(max_length=255) address = ForeignObject( Address, on_delete=CASCADE, null=True, from_fields=["customer_id", "company"], to_fields=["tiers_id", "company"] ) class Meta(object): unique_together = [ ("company", "customer_id"), ] class Contact(models.Model): company_code = models.CharField(max_length=1) customer_code = models.IntegerField() surname = models.CharField(max_length=255) # virtual field customer = ForeignObject( Customer, on_delete=CASCADE, related_name='contacts', # not the same order as for Customer -> address which is (customer, company) to_fields=["company", "customer_id"], from_fields=["company_code", "customer_code"] # with same order as for Customer, the bug does not trigger # to_fields = ["customer_id", "company"], # from_fields = ["customer_code", "company_code"] ) class PhoneNumber(models.Model): num = models.CharField(max_length=32) type_number = models.IntegerField() contact = models.ForeignKey(Contact, on_delete=CASCADE, related_name='phonenumbers') }}} with this models.py, the different orders of the fields Contact.customer can break all query like `PhoneNumber.objects.filter(contact__customer__address=a)`.(see comment in Customer) I found the problem is in django.db.models.query.Query.trim_joins line 1444 on release 1.9.5 {{{#!python3 targets = tuple(r[0] for r in info.join_field.related_fields if r[1].column in cur_targets) }}} we see that the new targets is created using the previous used targets, but the order of the previous target is not kept, and the order of to_fields is used to define the new targets. this lead to a query like : {{{#!sql SELECT "buggyapp_phonenumber"."id", "buggyapp_phonenumber"."num", "buggyapp_phonenumber"."type_number", "buggyapp_phonenumber"."contact_id" FROM "buggyapp_phonenumber" INNER JOIN "buggyapp_contact" ON ("buggyapp_phonenumber"."contact_id" = "buggyapp_contact"."id") WHERE ("buggyapp_contact"."company_code" = 10 AND "buggyapp_contact"."customer_code" = a) }}} instead of {{{#!sql SELECT "buggyapp_phonenumber"."id", "buggyapp_phonenumber"."num", "buggyapp_phonenumber"."type_number", "buggyapp_phonenumber"."contact_id" FROM "buggyapp_phonenumber" INNER JOIN "buggyapp_contact" ON ("buggyapp_phonenumber"."contact_id" = "buggyapp_contact"."id") WHERE ("buggyapp_contact"."customer_code" = 10 AND "buggyapp_contact"."company_code" = a) }}} the where part have the values order kept, but the fields order is inconsistent and it end with customer_code = 'a' instead of 10. the attached files contains a basic app that trigger the bug, and a patch in trim_query that will build the new targets with same order as the previous. the patch is made for django 1.9.5 -- Ticket URL: <https://code.djangoproject.com/ticket/26515> 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 django-updates+unsubscr...@googlegroups.com. To post to this group, send email to django-updates@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/050.63c7129d388d76d733ef91e65ef400ef%40djangoproject.com. For more options, visit https://groups.google.com/d/optout.