diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 6946c273c72..d182242ce4d 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1438,7 +1438,8 @@ class Query(object): cur_targets = set(t.column for t in targets) if not cur_targets.issubset(join_targets): break - targets = tuple(r[0] for r in info.join_field.related_fields if r[1].column in cur_targets) + targets_dict = {r[1].column: r[0] for r in info.join_field.related_fields if r[1].column in cur_targets} + targets = tuple(targets_dict[t.column] for t in targets) self.unref_alias(joins.pop()) return targets, joins[-1], joins diff --git a/tests/foreign_object/models/__init__.py b/tests/foreign_object/models/__init__.py index 8fe4ff10df4..652a2a30a14 100644 --- a/tests/foreign_object/models/__init__.py +++ b/tests/foreign_object/models/__init__.py @@ -1,10 +1,12 @@ from .article import ( Article, ArticleIdea, ArticleTag, ArticleTranslation, NewsArticle, ) +from .customers import Address, Contact, Customer from .empty_join import SlugPage from .person import Country, Friendship, Group, Membership, Person __all__ = [ - 'Article', 'ArticleIdea', 'ArticleTag', 'ArticleTranslation', 'Country', - 'Friendship', 'Group', 'Membership', 'NewsArticle', 'Person', 'SlugPage', + 'Address', 'Article', 'ArticleIdea', 'ArticleTag', 'ArticleTranslation', + 'Contact', 'Country', 'Customer', 'Friendship', 'Group', 'Membership', + 'NewsArticle', 'Person', 'SlugPage', ] diff --git a/tests/foreign_object/models/customers.py b/tests/foreign_object/models/customers.py new file mode 100644 index 00000000000..24c080f4074 --- /dev/null +++ b/tests/foreign_object/models/customers.py @@ -0,0 +1,38 @@ +from django.db import models +from django.db.models.fields.related import ForeignObject + + +class Address(models.Model): + company = models.CharField(max_length=1) + customer_id = models.IntegerField() + + class Meta: + unique_together = [ + ('company', 'customer_id'), + ] + + +class Customer(models.Model): + company = models.CharField(max_length=1) + customer_id = models.IntegerField() + address = ForeignObject( + Address, models.CASCADE, null=True, + # order mismatches the Contact ForeignObject. + from_fields=['company', 'customer_id'], + to_fields=['company', 'customer_id'], + ) + + class Meta: + unique_together = [ + ('company', 'customer_id'), + ] + + +class Contact(models.Model): + company_code = models.CharField(max_length=1) + customer_code = models.IntegerField() + customer = ForeignObject( + Customer, models.CASCADE, related_name='contacts', + to_fields=['customer_id', 'company'], + from_fields=['customer_code', 'company_code'], + ) diff --git a/tests/foreign_object/test_agnostic_order_trimjoin.py b/tests/foreign_object/test_agnostic_order_trimjoin.py new file mode 100644 index 00000000000..d3b7184be61 --- /dev/null +++ b/tests/foreign_object/test_agnostic_order_trimjoin.py @@ -0,0 +1,28 @@ +from operator import attrgetter + +from django.test.testcases import TestCase + +from .models import Address, Contact, Customer + + +class TestLookupQuery(TestCase): + + @classmethod + def setUpTestData(cls): + cls.address = Address.objects.create(company=1, customer_id=20) + cls.customer1 = Customer.objects.create(company=1, customer_id=20) + cls.contact1 = Contact.objects.create(company_code=1, customer_code=20) + + def test_deep_mixed_forward(self): + self.assertQuerysetEqual( + Address.objects.filter(customer__contacts=self.contact1), + [self.address.id], + attrgetter('id') + ) + + def test_deep_mixed_backward(self): + self.assertQuerysetEqual( + Contact.objects.filter(customer__address=self.address), + [self.contact1.id], + attrgetter('id') + )