diff options
| author | Tim Graham <timograham@gmail.com> | 2016-10-28 11:20:23 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-10-28 11:20:23 -0400 |
| commit | 1bc249c2a67c24fcd28436c85388eff1d826e305 (patch) | |
| tree | 429351ccffaed9711a00b8c4d6eae760c72ac453 /django/db/models/sql/query.py | |
| parent | 80e742d991b276023a3aa51a29ed757879051282 (diff) | |
Fixed #20939 -- Simplified query generation by converting QuerySet to Query.
Thanks Anssi Kääriäinen for the initial patch and Anssi, Simon Charette,
and Josh Smeaton for review.
Diffstat (limited to 'django/db/models/sql/query.py')
| -rw-r--r-- | django/db/models/sql/query.py | 48 |
1 files changed, 33 insertions, 15 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index f82eca3774..97e90aa082 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -143,6 +143,7 @@ class Query(object): self.standard_ordering = True self.used_aliases = set() self.filter_is_sticky = False + self.subquery = False # SQL-related attributes # Select and related select clauses are expressions to use in the @@ -319,6 +320,7 @@ class Query(object): else: obj.used_aliases = set() obj.filter_is_sticky = False + obj.subquery = self.subquery if 'alias_prefix' in self.__dict__: obj.alias_prefix = self.alias_prefix if 'subq_aliases' in self.__dict__: @@ -964,6 +966,9 @@ class Query(object): self.append_annotation_mask([alias]) self.annotations[alias] = annotation + def _prepare_as_filter_value(self): + return self.clone() + def prepare_lookup_value(self, value, lookups, can_reuse, allow_joins=True): # Default lookup if none given is exact. used_joins = [] @@ -974,8 +979,7 @@ class Query(object): if value is None: if lookups[-1] not in ('exact', 'iexact'): raise ValueError("Cannot use None as a query value") - lookups[-1] = 'isnull' - value = True + return True, ['isnull'], used_joins elif hasattr(value, 'resolve_expression'): pre_joins = self.alias_refcount.copy() value = value.resolve_expression(self, reuse=can_reuse, allow_joins=allow_joins) @@ -997,11 +1001,8 @@ class Query(object): # Subqueries need to use a different set of aliases than the # outer query. Call bump_prefix to change aliases of the inner # query (the value). - if hasattr(value, 'query') and hasattr(value.query, 'bump_prefix'): - value = value._clone() - value.query.bump_prefix(self) - if hasattr(value, 'bump_prefix'): - value = value.clone() + if hasattr(value, '_prepare_as_filter_value'): + value = value._prepare_as_filter_value() value.bump_prefix(self) # For Oracle '' is equivalent to null. The check needs to be done # at this stage because join promotion can't be done at compiler @@ -1049,14 +1050,20 @@ class Query(object): Checks the type of object passed to query relations. """ if field.is_relation: - # 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, field): - raise ValueError( - 'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' % - (value.model._meta.object_name, opts.object_name) - ) + # Check that the field and the queryset use the same model in a + # query like .filter(author=Author.objects.all()). For example, the + # opts would be Author's (from the author field) and value.model + # would be Author.objects.all() queryset's .model (Author also). + # The field is the related field on the lhs side. + # If _forced_pk isn't set, this isn't a queryset query or values() + # or values_list() was specified by the developer in which case + # that choice is trusted. + if (getattr(value, '_forced_pk', False) and + not check_rel_lookup_compatibility(value.model, opts, field)): + raise ValueError( + 'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' % + (value.model._meta.object_name, opts.object_name) + ) elif hasattr(value, '_meta'): self.check_query_object_type(value, opts, field) elif hasattr(value, '__iter__'): @@ -2005,6 +2012,17 @@ class Query(object): else: return field.null + def as_subquery_filter(self, db): + self._db = db + self.subquery = True + # It's safe to drop ordering if the queryset isn't using slicing, + # distinct(*fields) or select_for_update(). + if (self.low_mark == 0 and self.high_mark is None and + not self.distinct_fields and + not self.select_for_update): + self.clear_ordering(True) + return self + def get_order_dir(field, default='ASC'): """ |
