diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 72948f9..d940c97 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -456,7 +456,7 @@ class SQLCompiler(object):
         """
         if not alias:
             alias = self.query.get_initial_alias()
-        field, target, opts, joins, _, _ = self.query.setup_joins(pieces,
+        field, target, opts, joins, _, _, _ = self.query.setup_joins(pieces,
                 opts, alias, False)
         alias = joins[-1]
         col = target.column
diff --git a/django/db/models/sql/expressions.py b/django/db/models/sql/expressions.py
index 1bbf742..af1ed63 100644
--- a/django/db/models/sql/expressions.py
+++ b/django/db/models/sql/expressions.py
@@ -44,7 +44,7 @@ class SQLEvaluator(object):
             self.cols[node] = query.aggregate_select[node.name]
         else:
             try:
-                field, source, opts, join_list, last, _ = query.setup_joins(
+                field, source, opts, join_list, last, _, _ = query.setup_joins(
                     field_list, query.get_meta(),
                     query.get_initial_alias(), False)
                 col, _, join_list = query.trim_joins(source, join_list, last, False)
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index a78df34..630a270 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -700,6 +700,14 @@ class Query(object):
             return True
         return False
 
+    def demote_alias(self, alias):
+        """
+        Demotes the join type of an alias to an inner join.
+        """
+        data = list(self.alias_map[alias])
+        data[JOIN_TYPE] = self.INNER
+        self.alias_map[alias] = tuple(data)
+
     def promote_alias_chain(self, chain, must_promote=False):
         """
         Walks along a chain of aliases, promoting the first nullable join and
@@ -1007,7 +1015,7 @@ class Query(object):
             #   - this is an annotation over a model field
             # then we need to explore the joins that are required.
 
-            field, source, opts, join_list, last, _ = self.setup_joins(
+            field, source, opts, join_list, last, _, _ = self.setup_joins(
                 field_list, opts, self.get_initial_alias(), False)
 
             # Process the join chain to see if it can be trimmed
@@ -1119,7 +1127,7 @@ class Query(object):
         allow_many = trim or not negate
 
         try:
-            field, target, opts, join_list, last, extra_filters = self.setup_joins(
+            field, target, opts, join_list, last, extra_filters, allow_trim_join = self.setup_joins(
                     parts, opts, alias, True, allow_many, allow_explicit_fk=True,
                     can_reuse=can_reuse, negate=negate,
                     process_extras=process_extras)
@@ -1139,6 +1147,13 @@ class Query(object):
             self.promote_alias_chain(join_list)
             join_promote = True
 
+        # If we have a one2one or many2one field, we can trim the left outer
+        # join from the end of a list of joins.
+        # In order to do this, we convert alias join type back to INNER and
+        # trim_joins later will do the strip for us.
+        if allow_trim_join and field.rel:
+            self.demote_alias(join_list[-1])
+
         # Process the join list to see if we can remove any inner joins from
         # the far end (fewer tables in a query is better).
         nonnull_comparison = (lookup_type == 'isnull' and value is False)
@@ -1295,6 +1310,7 @@ class Query(object):
         dupe_set = set()
         exclusions = set()
         extra_filters = []
+        allow_trim_join = True
         int_alias = None
         for pos, name in enumerate(names):
             if int_alias is not None:
@@ -1318,6 +1334,11 @@ class Query(object):
                     raise FieldError("Cannot resolve keyword %r into field. "
                             "Choices are: %s" % (name, ", ".join(names)))
 
+            # presence of indirect field in the filter requires
+            # left outer join for isnull
+            if not direct and allow_trim_join:
+                allow_trim_join = False
+
             if not allow_many and (m2m or not direct):
                 for alias in joins:
                     self.unref_alias(alias)
@@ -1359,6 +1380,8 @@ class Query(object):
                 extra_filters.extend(field.extra_filters(names, pos, negate))
             if direct:
                 if m2m:
+                    # null query on m2mfield requires outer join
+                    allow_trim_join = False
                     # Many-to-many field defined on the current model.
                     if cached_data:
                         (table1, from_col1, to_col1, table2, from_col2,
@@ -1479,7 +1502,7 @@ class Query(object):
             else:
                 raise FieldError("Join on field %r not permitted." % name)
 
-        return field, target, opts, joins, last, extra_filters
+        return field, target, opts, joins, last, extra_filters, allow_trim_join
 
     def trim_joins(self, target, join_list, last, trim, nonnull_check=False):
         """
@@ -1648,7 +1671,7 @@ class Query(object):
 
         try:
             for name in field_names:
-                field, target, u2, joins, u3, u4 = self.setup_joins(
+                field, target, u2, joins, u3, u4, allow_trim_join = self.setup_joins(
                         name.split(LOOKUP_SEP), opts, alias, False, allow_m2m,
                         True)
                 final_alias = joins[-1]
@@ -1930,7 +1953,7 @@ class Query(object):
         """
         opts = self.model._meta
         alias = self.get_initial_alias()
-        field, col, opts, joins, last, extra = self.setup_joins(
+        field, col, opts, joins, last, extra, allow_trim_join = self.setup_joins(
                 start.split(LOOKUP_SEP), opts, alias, False)
         select_col = self.alias_map[joins[1]][LHS_JOIN_COL]
         select_alias = alias
