Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

queryset-refactor: Interpret qs.filter(foo=None) to be the same as qs…

….filter(foo__isnull=True). Refs #2737.

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6760 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a2418176fd22f1e4af0377c70c5b2d8955b71afe 1 parent 648a3d8
Malcolm Tredinnick malcolmt authored
15 django/db/models/sql/query.py
View
@@ -679,13 +679,13 @@ def add_filter(self, filter_expr, connection=AND, negate=False):
else:
lookup_type = parts.pop()
- # Interpret '__exact=None' as the sql '= NULL'; otherwise, reject all
+ # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
# uses of None as a query value.
- # FIXME: Weren't we going to change this so that '__exact=None' was the
- # same as '__isnull=True'? Need to check the conclusion of the mailing
- # list thread.
- if value is None and lookup_type != 'exact':
- raise ValueError("Cannot use None as a query value")
+ if value is None:
+ if lookup_type != 'exact':
+ raise ValueError("Cannot use None as a query value")
+ lookup_type = 'isnull'
+ value = True
elif callable(value):
value = value()
@@ -708,7 +708,8 @@ def add_filter(self, filter_expr, connection=AND, negate=False):
alias = join[LHS_ALIAS]
col = join[LHS_JOIN_COL]
- if (lookup_type == 'isnull' and value is True):
+ if lookup_type == 'isnull' and value is True and (len(join_list) > 1 or
+ len(join_list[0]) > 1):
# If the comparison is against NULL, we need to use a left outer
# join when connecting to the previous model. We make that
# adjustment here. We don't do this unless needed because it's less
15 docs/db-api.txt
View
@@ -1137,7 +1137,12 @@ Examples::
SQL equivalents::
SELECT ... WHERE id = 14;
- SELECT ... WHERE id = NULL;
+ SELECT ... WHERE id IS NULL;
+
+**New in Django development version:** The semantics of ``id__exact=None`` have
+changed in the development version. Previously, it was (intentionally)
+converted to ``WHERE id = NULL`` at the SQL level, which would never match
+anything. It has now been changed to behave the same as ``id__isnull=True``.
iexact
~~~~~~
@@ -1367,14 +1372,6 @@ SQL equivalent::
SELECT ... WHERE pub_date IS NULL;
-.. admonition:: ``__isnull=True`` vs ``__exact=None``
-
- There is an important difference between ``__isnull=True`` and
- ``__exact=None``. ``__exact=None`` will *always* return an empty result
- set, because SQL requires that no value is equal to ``NULL``.
- ``__isnull`` determines if the field is currently holding the value
- of ``NULL`` without performing a comparison.
-
search
~~~~~~
5 tests/modeltests/many_to_one_null/models.py
View
@@ -80,6 +80,11 @@ def __unicode__(self):
>>> Article.objects.filter(reporter__isnull=True)
[<Article: Third>]
+# We can achieve the same thing by filtering for the case where the reporter is
+# None.
+>>> Article.objects.filter(reporter=None)
+[<Article: Third>]
+
# Set the reporter for the Third article
>>> r.article_set.add(a3)
>>> r.article_set.all()
7 tests/regressiontests/null_queries/models.py
View
@@ -24,10 +24,15 @@ def __unicode__(self):
>>> c2 = Choice(poll=p1, choice='Why Not?')
>>> c2.save()
-# Exact query with value None returns nothing (=NULL in sql)
+# Exact query with value None returns nothing ("is NULL" in sql, but every 'id'
+# field has a value).
>>> Choice.objects.filter(id__exact=None)
[]
+Excluding the previous result returns everything.
+>>> Choice.objects.exclude(id=None).order_by('id')
+[<Choice: Choice: Because. in poll Q: Why? >, <Choice: Choice: Why Not? in poll Q: Why? >]
+
# Valid query, but fails because foo isn't a keyword
>>> Choice.objects.filter(foo__exact=None)
Traceback (most recent call last):
Please sign in to comment.
Something went wrong with that request. Please try again.