Ticket #20535: #20535-Unnecessary_join created for intermediate_table_between_two_M2M_tables.diff

File #20535-Unnecessary_join created for intermediate_table_between_two_M2M_tables.diff, 3.4 KB (added by Kronuz, 2 years ago)
  • django/db/models/fields/related.py

    class ManyToOneRel(ForeignObjectRel): 
    884884    def set_field_name(self):
    885885        self.field_name = self.field_name or self.to._meta.pk.name
    886886
     887    @property
     888    def related_fields(self):
     889        return [tuple([self.get_related_field(), self.field])]
     890
     891    @property
     892    def foreign_related_fields(self):
     893        return tuple([self.field])
     894
    887895
    888896class OneToOneRel(ManyToOneRel):
    889897    def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None,
  • django/db/models/sql/query.py

    diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
    index 0a41525..492120c 100644
    class Query(object): 
    13561356        # Then, add the path to the query's joins. Note that we can't trim
    13571357        # joins at this stage - we will need the information about join type
    13581358        # of the trimmed joins.
     1359        last = None
     1360        final_path = []
    13591361        for pos, join in enumerate(path):
    1360             opts = join.to_opts
    13611362            if join.direct:
    13621363                nullable = self.is_nullable(join.join_field)
    13631364            else:
    13641365                nullable = True
    1365             connection = alias, opts.db_table, join.join_field.get_joining_columns()
     1366            final_path.append(join)
     1367            joining_columns = join.join_field.get_joining_columns()
     1368            next = path[pos + 1] if pos < len(path) - 1 else None
     1369            if last and next:
     1370                # Try removing previous join if the current one maps directly
     1371                # to the next one (much in the way trim_joins does it):
     1372                cur_targets = set(t.column for t in zip(*join.join_field.related_fields)[0])
     1373                join_targets = set(t.column for t in next.join_field.foreign_related_fields)
     1374                if cur_targets.issubset(join_targets):
     1375                    self.unref_alias(joins.pop())
     1376                    alias = joins[-1]
     1377                    final_path.pop()
     1378                    joining_columns = tuple((c[0], joining_columns[i][1]) for i, c in enumerate(next.join_field.get_joining_columns()))
     1379            last = join
     1380            opts = join.to_opts
     1381            connection = alias, opts.db_table, joining_columns
    13661382            reuse = can_reuse if join.m2m else None
    13671383            alias = self.join(connection, reuse=reuse,
    13681384                              nullable=nullable, join_field=join.join_field)
    13691385            joins.append(alias)
    13701386        if hasattr(final_field, 'field'):
    13711387            final_field = final_field.field
    1372         return final_field, targets, opts, joins, path
     1388        return final_field, targets, opts, joins, final_path
    13731389
    13741390    def trim_joins(self, targets, joins, path):
    13751391        """
  • tests/m2m_through/tests.py

    diff --git a/tests/m2m_through/tests.py b/tests/m2m_through/tests.py
    index 259dc68..bbf7595 100644
    class M2mThroughTests(TestCase): 
    343343            ],
    344344            attrgetter("name")
    345345        )
     346
     347    def test_ticket_20535_join_trimming(self):
     348        qs = Friendship.objects.filter(first__rel_to_set__first__id=1)
     349        self.assertEqual(str(qs.query).count('JOIN'), 1)
     350
     351    def test_ticket_20535_no_join_trimming(self):
     352        qs = Friendship.objects.filter(first__rel_to_set__first__id=1, first__name='Jane')
     353        self.assertEqual(str(qs.query).count('JOIN'), 2)
Back to Top