mirror of
https://github.com/django/django.git
synced 2025-10-29 16:46:11 +00:00
Added support for modifying the effect of `DISTINCT` clauses so they
only consider some fields (PostgreSQL only). For this, the ``distinct()`` QuerySet method now accepts an optional list of model fields names and generates ``DISTINCT ON`` clauses on these cases. Thanks Jeffrey Gelens and Anssi Kääriäinen for their work. Fixes #6422. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17244 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -127,6 +127,7 @@ class Query(object):
|
||||
self.order_by = []
|
||||
self.low_mark, self.high_mark = 0, None # Used for offset/limit
|
||||
self.distinct = False
|
||||
self.distinct_fields = []
|
||||
self.select_for_update = False
|
||||
self.select_for_update_nowait = False
|
||||
self.select_related = False
|
||||
@@ -265,6 +266,7 @@ class Query(object):
|
||||
obj.order_by = self.order_by[:]
|
||||
obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
|
||||
obj.distinct = self.distinct
|
||||
obj.distinct_fields = self.distinct_fields[:]
|
||||
obj.select_for_update = self.select_for_update
|
||||
obj.select_for_update_nowait = self.select_for_update_nowait
|
||||
obj.select_related = self.select_related
|
||||
@@ -298,6 +300,7 @@ class Query(object):
|
||||
else:
|
||||
obj.used_aliases = set()
|
||||
obj.filter_is_sticky = False
|
||||
|
||||
obj.__dict__.update(kwargs)
|
||||
if hasattr(obj, '_setup_query'):
|
||||
obj._setup_query()
|
||||
@@ -393,7 +396,7 @@ class Query(object):
|
||||
Performs a COUNT() query using the current filter constraints.
|
||||
"""
|
||||
obj = self.clone()
|
||||
if len(self.select) > 1 or self.aggregate_select:
|
||||
if len(self.select) > 1 or self.aggregate_select or (self.distinct and self.distinct_fields):
|
||||
# If a select clause exists, then the query has already started to
|
||||
# specify the columns that are to be returned.
|
||||
# In this case, we need to use a subquery to evaluate the count.
|
||||
@@ -452,6 +455,8 @@ class Query(object):
|
||||
"Cannot combine queries once a slice has been taken."
|
||||
assert self.distinct == rhs.distinct, \
|
||||
"Cannot combine a unique query with a non-unique query."
|
||||
assert self.distinct_fields == rhs.distinct_fields, \
|
||||
"Cannot combine queries with different distinct fields."
|
||||
|
||||
self.remove_inherited_models()
|
||||
# Work out how to relabel the rhs aliases, if necessary.
|
||||
@@ -674,9 +679,9 @@ class Query(object):
|
||||
""" Increases the reference count for this alias. """
|
||||
self.alias_refcount[alias] += 1
|
||||
|
||||
def unref_alias(self, alias):
|
||||
def unref_alias(self, alias, amount=1):
|
||||
""" Decreases the reference count for this alias. """
|
||||
self.alias_refcount[alias] -= 1
|
||||
self.alias_refcount[alias] -= amount
|
||||
|
||||
def promote_alias(self, alias, unconditional=False):
|
||||
"""
|
||||
@@ -705,6 +710,15 @@ class Query(object):
|
||||
if self.promote_alias(alias, must_promote):
|
||||
must_promote = True
|
||||
|
||||
def reset_refcounts(self, to_counts):
|
||||
"""
|
||||
This method will reset reference counts for aliases so that they match
|
||||
the value passed in :param to_counts:.
|
||||
"""
|
||||
for alias, cur_refcount in self.alias_refcount.copy().items():
|
||||
unref_amount = cur_refcount - to_counts.get(alias, 0)
|
||||
self.unref_alias(alias, unref_amount)
|
||||
|
||||
def promote_unused_aliases(self, initial_refcounts, used_aliases):
|
||||
"""
|
||||
Given a "before" copy of the alias_refcounts dictionary (as
|
||||
@@ -832,7 +846,8 @@ class Query(object):
|
||||
def count_active_tables(self):
|
||||
"""
|
||||
Returns the number of tables in this query with a non-zero reference
|
||||
count.
|
||||
count. Note that after execution, the reference counts are zeroed, so
|
||||
tables added in compiler will not be seen by this method.
|
||||
"""
|
||||
return len([1 for count in self.alias_refcount.itervalues() if count])
|
||||
|
||||
@@ -1596,6 +1611,13 @@ class Query(object):
|
||||
self.select = []
|
||||
self.select_fields = []
|
||||
|
||||
def add_distinct_fields(self, *field_names):
|
||||
"""
|
||||
Adds and resolves the given fields to the query's "distinct on" clause.
|
||||
"""
|
||||
self.distinct_fields = field_names
|
||||
self.distinct = True
|
||||
|
||||
def add_fields(self, field_names, allow_m2m=True):
|
||||
"""
|
||||
Adds the given (model) fields to the select set. The field names are
|
||||
|
||||
Reference in New Issue
Block a user