From e2296e7f0acf9c50868ab997ba565c1f47beb45b Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sat, 28 May 2016 09:14:02 -0400 Subject: [PATCH] Fixed #26667 -- Fixed a regression in queries on a OneToOneField that has to_field and primary_key=True. Thanks Simon Charette for review. --- django/db/models/fields/related_lookups.py | 11 ++++++----- docs/releases/1.9.7.txt | 3 +++ tests/one_to_one/models.py | 6 +++++- tests/one_to_one/tests.py | 10 ++++++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/django/db/models/fields/related_lookups.py b/django/db/models/fields/related_lookups.py index 3123e94073..5454031c2a 100644 --- a/django/db/models/fields/related_lookups.py +++ b/django/db/models/fields/related_lookups.py @@ -24,15 +24,16 @@ def get_normalized_value(value, lhs): from django.db.models import Model if isinstance(value, Model): value_list = [] - # A case like Restaurant.objects.filter(place=restaurant_instance), - # where place is a OneToOneField and the primary key of Restaurant. - if getattr(lhs.output_field, 'primary_key', False): - return (value.pk,) sources = lhs.output_field.get_path_info()[-1].target_fields for source in sources: while not isinstance(value, source.model) and source.remote_field: source = source.remote_field.model._meta.get_field(source.remote_field.field_name) - value_list.append(getattr(value, source.attname)) + try: + value_list.append(getattr(value, source.attname)) + except AttributeError: + # A case like Restaurant.objects.filter(place=restaurant_instance), + # where place is a OneToOneField and the primary key of Restaurant. + return (value.pk,) return tuple(value_list) if not isinstance(value, tuple): return (value,) diff --git a/docs/releases/1.9.7.txt b/docs/releases/1.9.7.txt index 514d83bf15..a61f4ed9dd 100644 --- a/docs/releases/1.9.7.txt +++ b/docs/releases/1.9.7.txt @@ -26,3 +26,6 @@ Bugfixes * Fixed ``dbshell`` crash on PostgreSQL with an empty database name (:ticket:`26698`). + +* Fixed a regression in queries on a ``OneToOneField`` that has ``to_field`` + and ``primary_key=True`` (:ticket:`26667`). diff --git a/tests/one_to_one/models.py b/tests/one_to_one/models.py index 2742fb186e..0f4aa70f6c 100644 --- a/tests/one_to_one/models.py +++ b/tests/one_to_one/models.py @@ -83,7 +83,7 @@ class MultiModel(models.Model): class Target(models.Model): - name = models.CharField(max_length=50) + name = models.CharField(max_length=50, unique=True) class Pointer(models.Model): @@ -98,6 +98,10 @@ class HiddenPointer(models.Model): target = models.OneToOneField(Target, models.CASCADE, related_name='hidden+') +class ToFieldPointer(models.Model): + target = models.OneToOneField(Target, models.CASCADE, to_field='name', primary_key=True) + + # Test related objects visibility. class SchoolManager(models.Manager): def get_queryset(self): diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py index 7e8c9c5dc3..2cb6568221 100644 --- a/tests/one_to_one/tests.py +++ b/tests/one_to_one/tests.py @@ -6,8 +6,8 @@ from django.utils.deprecation import RemovedInDjango20Warning from .models import ( Bar, Director, Favorites, HiddenPointer, ManualPrimaryKey, MultiModel, - Place, Pointer, RelatedModel, Restaurant, School, Target, UndergroundBar, - Waiter, + Place, Pointer, RelatedModel, Restaurant, School, Target, ToFieldPointer, + UndergroundBar, Waiter, ) @@ -530,3 +530,9 @@ class OneToOneTests(TestCase): r = Restaurant.objects.first() r2 = Restaurant.objects.filter(pk__exact=r).first() self.assertEqual(r, r2) + + def test_primary_key_to_field_filter(self): + target = Target.objects.create(name='foo') + pointer = ToFieldPointer.objects.create(target=target) + self.assertQuerysetEqual(ToFieldPointer.objects.filter(target=target), [pointer], lambda x: x) + self.assertQuerysetEqual(ToFieldPointer.objects.filter(pk__exact=pointer), [pointer], lambda x: x)