Fixed #26196 -- Made sure __in lookups use to_field as default.

Thanks Simon Charette for the test.
This commit is contained in:
Anssi Kääriäinen 2016-02-11 08:39:37 +02:00 committed by Tim Graham
parent 04e13c8913
commit 46ecfb9b3a
6 changed files with 25 additions and 4 deletions

View File

@ -210,7 +210,7 @@ class BaseExpression(object):
]) ])
return c return c
def _prepare(self): def _prepare(self, field):
""" """
Hook used by Field.get_prep_lookup() to do custom preparation. Hook used by Field.get_prep_lookup() to do custom preparation.
""" """

View File

@ -740,7 +740,7 @@ class Field(RegisterLookupMixin):
Perform preliminary non-db specific lookup checks and conversions Perform preliminary non-db specific lookup checks and conversions
""" """
if hasattr(value, '_prepare'): if hasattr(value, '_prepare'):
return value._prepare() return value._prepare(self)
if lookup_type in { if lookup_type in {
'iexact', 'contains', 'icontains', 'iexact', 'contains', 'icontains',

View File

@ -1108,12 +1108,18 @@ class QuerySet(object):
for field, objects in other._known_related_objects.items(): for field, objects in other._known_related_objects.items():
self._known_related_objects.setdefault(field, {}).update(objects) self._known_related_objects.setdefault(field, {}).update(objects)
def _prepare(self): def _prepare(self, field):
if self._fields is not None: if self._fields is not None:
# values() queryset can only be used as nested queries # values() queryset can only be used as nested queries
# if they are set up to select only a single field. # if they are set up to select only a single field.
if len(self._fields or self.model._meta.concrete_fields) > 1: if len(self._fields or self.model._meta.concrete_fields) > 1:
raise TypeError('Cannot use multi-field values as a filter value.') raise TypeError('Cannot use multi-field values as a filter value.')
else:
# If the query is used as a subquery for a ForeignKey with non-pk
# target field, make sure to select the target field in the subquery.
foreign_fields = getattr(field, 'foreign_related_fields', ())
if len(foreign_fields) == 1 and not foreign_fields[0].primary_key:
return self.values(foreign_fields[0].name)
return self return self
def _as_sql(self, connection): def _as_sql(self, connection):

View File

@ -235,7 +235,7 @@ class Query(object):
memo[id(self)] = result memo[id(self)] = result
return result return result
def _prepare(self): def _prepare(self, field):
return self return self
def get_compiler(self, using=None, connection=None): def get_compiler(self, using=None, connection=None):

View File

@ -21,3 +21,6 @@ Bugfixes
* Fixed a regression for cases where * Fixed a regression for cases where
``ForeignObject.get_extra_descriptor_filter()`` returned a ``Q`` object ``ForeignObject.get_extra_descriptor_filter()`` returned a ``Q`` object
(:ticket:`26153`). (:ticket:`26153`).
* Fixed regression with an ``__in=qs`` lookup for a ``ForeignKey`` with
``to_field`` set (:ticket:`26196`).

View File

@ -2416,6 +2416,18 @@ class ToFieldTests(TestCase):
{lunch, dinner}, {lunch, dinner},
) )
def test_in_subquery(self):
apple = Food.objects.create(name="apple")
lunch = Eaten.objects.create(food=apple, meal="lunch")
self.assertEqual(
set(Eaten.objects.filter(food__in=Food.objects.filter(name='apple'))),
{lunch}
)
self.assertEqual(
set(Eaten.objects.filter(food__in=Food.objects.filter(name='apple').values('eaten__meal'))),
set()
)
def test_reverse_in(self): def test_reverse_in(self):
apple = Food.objects.create(name="apple") apple = Food.objects.create(name="apple")
pear = Food.objects.create(name="pear") pear = Food.objects.create(name="pear")