summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabe Jackson <gabejackson@cxg.ch>2014-09-02 15:30:53 +0200
committerTim Graham <timograham@gmail.com>2014-10-21 10:02:51 -0400
commit0e16c3e3cd61b119e067b369f3ff928a2e4045b4 (patch)
treea1c471dc0513715cdb0c72a3cfd850c5bf5340c4
parent3a34e45fdbecc1f1ead0a3c2f1c01111a865710e (diff)
Fixed #23396 -- Ensured ValueQuerySets are not checked by check_related_objects.
-rw-r--r--django/db/models/query.py16
-rw-r--r--django/db/models/sql/query.py26
-rw-r--r--tests/queries/tests.py8
3 files changed, 35 insertions, 15 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 85f1981662..d35f54b0b5 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1052,6 +1052,15 @@ class QuerySet(object):
"""
return self.query.has_filters()
+ def is_compatible_query_object_type(self, opts):
+ model = self.model
+ return (
+ model == opts.concrete_model or
+ opts.concrete_model in model._meta.get_parent_list() or
+ model in opts.get_parent_list()
+ )
+ is_compatible_query_object_type.queryset_only = True
+
class InstanceCheckMeta(type):
def __instancecheck__(self, instance):
@@ -1209,6 +1218,13 @@ class ValuesQuerySet(QuerySet):
% self.__class__.__name__)
return self
+ def is_compatible_query_object_type(self, opts):
+ """
+ ValueQuerySets do not need to be checked for compatibility.
+ We trust that users of ValueQuerySets know what they are doing.
+ """
+ return True
+
class ValuesListQuerySet(ValuesQuerySet):
def iterator(self):
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 2e7b37d423..08af1fb008 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1094,21 +1094,17 @@ class Query(object):
Checks the type of object passed to query relations.
"""
if field.rel:
- # testing for iterable of models
- if hasattr(value, '__iter__'):
- # Check if the iterable has a model attribute, if so
- # it is likely something like a QuerySet.
- if hasattr(value, 'model') and hasattr(value.model, '_meta'):
- model = value.model
- if not (model == opts.concrete_model
- or opts.concrete_model in model._meta.get_parent_list()
- or model in opts.get_parent_list()):
- raise ValueError(
- 'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' %
- (model._meta.model_name, opts.object_name))
- else:
- for v in value:
- self.check_query_object_type(v, opts)
+ # QuerySets implement is_compatible_query_object_type() to
+ # determine compatibility with the given field.
+ if hasattr(value, 'is_compatible_query_object_type'):
+ if not value.is_compatible_query_object_type(opts):
+ raise ValueError(
+ 'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' %
+ (value.model._meta.model_name, opts.object_name)
+ )
+ elif hasattr(value, '__iter__'):
+ for v in value:
+ self.check_query_object_type(v, opts)
else:
# expecting single model instance here
self.check_query_object_type(value, opts)
diff --git a/tests/queries/tests.py b/tests/queries/tests.py
index d6f7aa38a0..8ec3fd5945 100644
--- a/tests/queries/tests.py
+++ b/tests/queries/tests.py
@@ -3487,6 +3487,14 @@ class RelatedLookupTypeTests(TestCase):
with self.assertNumQueries(0):
ObjectB.objects.filter(objecta__in=ObjectA.objects.all())
+ def test_values_queryset_lookup(self):
+ """
+ #23396 - Ensure ValueQuerySets are not checked for compatibility with the lookup field
+ """
+ self.assertQuerysetEqual(ObjectB.objects.filter(
+ objecta__in=ObjectB.objects.all().values_list('pk')
+ ).order_by('pk'), ['<ObjectB: ob>', '<ObjectB: pob>'])
+
class Ticket14056Tests(TestCase):
def test_ticket_14056(self):