Skip to content

Commit 6884713

Browse files
committed
Removed dupe_avoidance from sql/query and sql/compiler.py
The dupe avoidance logic was removed as it doesn't seem to do anything, it is complicated, and it has nearly zero documentation. The removal of dupe_avoidance allowed for refactoring of both the implementation and signature of Query.join(). This refactoring cascades again to some other parts. The most significant of them is the changes in qs.combine(), and compiler.select_related_descent().
1 parent 9bf0eed commit 6884713

File tree

6 files changed

+98
-211
lines changed

6 files changed

+98
-211
lines changed

django/db/models/fields/related.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,11 +1053,6 @@ def value_to_string(self, obj):
10531053
def contribute_to_class(self, cls, name):
10541054
super(ForeignKey, self).contribute_to_class(cls, name)
10551055
setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self))
1056-
if isinstance(self.rel.to, six.string_types):
1057-
target = self.rel.to
1058-
else:
1059-
target = self.rel.to._meta.db_table
1060-
cls._meta.duplicate_targets[self.column] = (target, "o2m")
10611056

10621057
def contribute_to_related_class(self, cls, related):
10631058
# Internal FK's - i.e., those with a related name ending with '+' -
@@ -1293,12 +1288,6 @@ def resolve_through_model(field, model, cls):
12931288
field.rel.through = model
12941289
add_lazy_relation(cls, self, self.rel.through, resolve_through_model)
12951290

1296-
if isinstance(self.rel.to, six.string_types):
1297-
target = self.rel.to
1298-
else:
1299-
target = self.rel.to._meta.db_table
1300-
cls._meta.duplicate_targets[self.column] = (target, "m2m")
1301-
13021291
def contribute_to_related_class(self, cls, related):
13031292
# Internal M2Ms (i.e., those with a related name ending with '+')
13041293
# and swapped models don't get a related descriptor.

django/db/models/options.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def __init__(self, meta, app_label=None):
5858
self.concrete_model = None
5959
self.swappable = None
6060
self.parents = SortedDict()
61-
self.duplicate_targets = {}
6261
self.auto_created = False
6362

6463
# To handle various inheritance situations, we need to track where
@@ -147,24 +146,6 @@ def _prepare(self, model):
147146
auto_created=True)
148147
model.add_to_class('id', auto)
149148

150-
# Determine any sets of fields that are pointing to the same targets
151-
# (e.g. two ForeignKeys to the same remote model). The query
152-
# construction code needs to know this. At the end of this,
153-
# self.duplicate_targets will map each duplicate field column to the
154-
# columns it duplicates.
155-
collections = {}
156-
for column, target in six.iteritems(self.duplicate_targets):
157-
try:
158-
collections[target].add(column)
159-
except KeyError:
160-
collections[target] = set([column])
161-
self.duplicate_targets = {}
162-
for elt in six.itervalues(collections):
163-
if len(elt) == 1:
164-
continue
165-
for column in elt:
166-
self.duplicate_targets[column] = elt.difference(set([column]))
167-
168149
def add_field(self, field):
169150
# Insert the given field in the order in which it was created, using
170151
# the "creation_counter" attribute of the field.

django/db/models/sql/compiler.py

Lines changed: 9 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from django.db.models.constants import LOOKUP_SEP
77
from django.db.models.query_utils import select_related_descend
88
from django.db.models.sql.constants import (SINGLE, MULTI, ORDER_DIR,
9-
GET_ITERATOR_CHUNK_SIZE, SelectInfo)
9+
GET_ITERATOR_CHUNK_SIZE, REUSE_ALL, SelectInfo)
1010
from django.db.models.sql.datastructures import EmptyResultSet
1111
from django.db.models.sql.expressions import SQLEvaluator
1212
from django.db.models.sql.query import get_order_dir, Query
@@ -457,7 +457,7 @@ def _setup_joins(self, pieces, opts, alias):
457457
if not alias:
458458
alias = self.query.get_initial_alias()
459459
field, target, opts, joins, _, _ = self.query.setup_joins(pieces,
460-
opts, alias, False)
460+
opts, alias, REUSE_ALL)
461461
# We will later on need to promote those joins that were added to the
462462
# query afresh above.
463463
joins_to_promote = [j for j in joins if self.query.alias_refcount[j] < 2]
@@ -574,8 +574,7 @@ def get_grouping(self):
574574
return result, params
575575

576576
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
577-
used=None, requested=None, restricted=None, nullable=None,
578-
dupe_set=None, avoid_set=None):
577+
requested=None, restricted=None, nullable=None):
579578
"""
580579
Fill in the information needed for a select_related query. The current
581580
depth is measured as the number of connections away from the root model
@@ -590,13 +589,6 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
590589
opts = self.query.get_meta()
591590
root_alias = self.query.get_initial_alias()
592591
self.query.related_select_cols = []
593-
if not used:
594-
used = set()
595-
if dupe_set is None:
596-
dupe_set = set()
597-
if avoid_set is None:
598-
avoid_set = set()
599-
orig_dupe_set = dupe_set
600592
only_load = self.query.get_loaded_field_names()
601593

602594
# Setup for the case when only particular related fields should be
@@ -616,12 +608,6 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
616608
if not select_related_descend(f, restricted, requested,
617609
only_load.get(field_model)):
618610
continue
619-
# The "avoid" set is aliases we want to avoid just for this
620-
# particular branch of the recursion. They aren't permanently
621-
# forbidden from reuse in the related selection tables (which is
622-
# what "used" specifies).
623-
avoid = avoid_set.copy()
624-
dupe_set = orig_dupe_set.copy()
625611
table = f.rel.to._meta.db_table
626612
promote = nullable or f.null
627613
if model:
@@ -637,31 +623,17 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
637623
int_opts = int_model._meta
638624
continue
639625
lhs_col = int_opts.parents[int_model].column
640-
dedupe = lhs_col in opts.duplicate_targets
641-
if dedupe:
642-
avoid.update(self.query.dupe_avoidance.get((id(opts), lhs_col),
643-
()))
644-
dupe_set.add((opts, lhs_col))
645626
int_opts = int_model._meta
646627
alias = self.query.join((alias, int_opts.db_table, lhs_col,
647-
int_opts.pk.column), exclusions=used,
628+
int_opts.pk.column),
648629
promote=promote)
649630
alias_chain.append(alias)
650-
for (dupe_opts, dupe_col) in dupe_set:
651-
self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias)
652631
else:
653632
alias = root_alias
654633

655-
dedupe = f.column in opts.duplicate_targets
656-
if dupe_set or dedupe:
657-
avoid.update(self.query.dupe_avoidance.get((id(opts), f.column), ()))
658-
if dedupe:
659-
dupe_set.add((opts, f.column))
660-
661634
alias = self.query.join((alias, table, f.column,
662635
f.rel.get_related_field().column),
663-
exclusions=used.union(avoid), promote=promote)
664-
used.add(alias)
636+
promote=promote)
665637
columns, aliases = self.get_default_columns(start_alias=alias,
666638
opts=f.rel.to._meta, as_pairs=True)
667639
self.query.related_select_cols.extend(
@@ -671,10 +643,8 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
671643
else:
672644
next = False
673645
new_nullable = f.null or promote
674-
for dupe_opts, dupe_col in dupe_set:
675-
self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias)
676646
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
677-
used, next, restricted, new_nullable, dupe_set, avoid)
647+
next, restricted, new_nullable)
678648

679649
if restricted:
680650
related_fields = [
@@ -686,14 +656,8 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
686656
if not select_related_descend(f, restricted, requested,
687657
only_load.get(model), reverse=True):
688658
continue
689-
# The "avoid" set is aliases we want to avoid just for this
690-
# particular branch of the recursion. They aren't permanently
691-
# forbidden from reuse in the related selection tables (which is
692-
# what "used" specifies).
693-
avoid = avoid_set.copy()
694-
dupe_set = orig_dupe_set.copy()
695-
table = model._meta.db_table
696659

660+
table = model._meta.db_table
697661
int_opts = opts
698662
alias = root_alias
699663
alias_chain = []
@@ -708,30 +672,16 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
708672
int_opts = int_model._meta
709673
continue
710674
lhs_col = int_opts.parents[int_model].column
711-
dedupe = lhs_col in opts.duplicate_targets
712-
if dedupe:
713-
avoid.update((self.query.dupe_avoidance.get(id(opts), lhs_col),
714-
()))
715-
dupe_set.add((opts, lhs_col))
716675
int_opts = int_model._meta
717676
alias = self.query.join(
718677
(alias, int_opts.db_table, lhs_col, int_opts.pk.column),
719-
exclusions=used, promote=True, reuse=used
678+
promote=True,
720679
)
721680
alias_chain.append(alias)
722-
for dupe_opts, dupe_col in dupe_set:
723-
self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias)
724-
dedupe = f.column in opts.duplicate_targets
725-
if dupe_set or dedupe:
726-
avoid.update(self.query.dupe_avoidance.get((id(opts), f.column), ()))
727-
if dedupe:
728-
dupe_set.add((opts, f.column))
729681
alias = self.query.join(
730682
(alias, table, f.rel.get_related_field().column, f.column),
731-
exclusions=used.union(avoid),
732683
promote=True
733684
)
734-
used.add(alias)
735685
columns, aliases = self.get_default_columns(start_alias=alias,
736686
opts=model._meta, as_pairs=True, local_only=True)
737687
self.query.related_select_cols.extend(
@@ -743,7 +693,7 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
743693
new_nullable = True
744694

745695
self.fill_related_selections(model._meta, table, cur_depth+1,
746-
used, next, restricted, new_nullable)
696+
next, restricted, new_nullable)
747697

748698
def deferred_to_columns(self):
749699
"""

django/db/models/sql/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@
3737
'ASC': ('ASC', 'DESC'),
3838
'DESC': ('DESC', 'ASC'),
3939
}
40+
41+
# A marker for join-reusability.
42+
REUSE_ALL = object()

0 commit comments

Comments
 (0)