--- django/db/models/fields/related.py
+++ django/db/models/fields/related.py
@@ -884,6 +884,14 @@ class ManyToOneRel(ForeignObjectRel):
     def set_field_name(self):
         self.field_name = self.field_name or self.to._meta.pk.name

+    @property
+    def related_fields(self):
+        return [tuple([self.get_related_field(), self.field])]
+
+    @property
+    def foreign_related_fields(self):
+        return tuple([self.field])
+

 class OneToOneRel(ManyToOneRel):
     def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None,
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 0a41525..492120c 100644
--- django/db/models/sql/query.py
+++ django/db/models/sql/query.py
@@ -1356,20 +1356,36 @@ class Query(object):
         # Then, add the path to the query's joins. Note that we can't trim
         # joins at this stage - we will need the information about join type
         # of the trimmed joins.
+        last = None
+        final_path = []
         for pos, join in enumerate(path):
-            opts = join.to_opts
             if join.direct:
                 nullable = self.is_nullable(join.join_field)
             else:
                 nullable = True
-            connection = alias, opts.db_table, join.join_field.get_joining_columns()
+            final_path.append(join)
+            joining_columns = join.join_field.get_joining_columns()
+            next = path[pos + 1] if pos < len(path) - 1 else None
+            if last and next:
+                # Try removing previous join if the current one maps directly
+                # to the next one (much in the way trim_joins does it):
+                cur_targets = set(t.column for t in zip(*join.join_field.related_fields)[0])
+                join_targets = set(t.column for t in next.join_field.foreign_related_fields)
+                if cur_targets.issubset(join_targets):
+                    self.unref_alias(joins.pop())
+                    alias = joins[-1]
+                    final_path.pop()
+                    joining_columns = tuple((c[0], joining_columns[i][1]) for i, c in enumerate(next.join_field.get_joining_columns()))
+            last = join
+            opts = join.to_opts
+            connection = alias, opts.db_table, joining_columns
             reuse = can_reuse if join.m2m else None
             alias = self.join(connection, reuse=reuse,
                               nullable=nullable, join_field=join.join_field)
             joins.append(alias)
         if hasattr(final_field, 'field'):
             final_field = final_field.field
-        return final_field, targets, opts, joins, path
+        return final_field, targets, opts, joins, final_path
 
     def trim_joins(self, targets, joins, path):
         """
diff --git a/tests/m2m_through/tests.py b/tests/m2m_through/tests.py
index 259dc68..bbf7595 100644
--- tests/m2m_through/tests.py
+++ tests/m2m_through/tests.py
@@ -343,3 +343,11 @@ class M2mThroughTests(TestCase):
             ],
             attrgetter("name")
         )
+
+    def test_ticket_20535_join_trimming(self):
+        qs = Friendship.objects.filter(first__rel_to_set__first__id=1)
+        self.assertEqual(str(qs.query).count('JOIN'), 1)
+
+    def test_ticket_20535_no_join_trimming(self):
+        qs = Friendship.objects.filter(first__rel_to_set__first__id=1, first__name='Jane')
+        self.assertEqual(str(qs.query).count('JOIN'), 2)
