Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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 authored November 30, 2007
15  django/db/models/sql/query.py
@@ -679,13 +679,13 @@ def add_filter(self, filter_expr, connection=AND, negate=False):
679 679
         else:
680 680
             lookup_type = parts.pop()
681 681
 
682  
-        # Interpret '__exact=None' as the sql '= NULL'; otherwise, reject all
  682
+        # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
683 683
         # uses of None as a query value.
684  
-        # FIXME: Weren't we going to change this so that '__exact=None' was the
685  
-        # same as '__isnull=True'? Need to check the conclusion of the mailing
686  
-        # list thread.
687  
-        if value is None and lookup_type != 'exact':
688  
-            raise ValueError("Cannot use None as a query value")
  684
+        if value is None:
  685
+            if lookup_type != 'exact':
  686
+                raise ValueError("Cannot use None as a query value")
  687
+            lookup_type = 'isnull'
  688
+            value = True
689 689
         elif callable(value):
690 690
             value = value()
691 691
 
@@ -708,7 +708,8 @@ def add_filter(self, filter_expr, connection=AND, negate=False):
708 708
                 alias = join[LHS_ALIAS]
709 709
                 col = join[LHS_JOIN_COL]
710 710
 
711  
-        if (lookup_type == 'isnull' and value is True):
  711
+        if lookup_type == 'isnull' and value is True and (len(join_list) > 1 or
  712
+                len(join_list[0]) > 1):
712 713
             # If the comparison is against NULL, we need to use a left outer
713 714
             # join when connecting to the previous model. We make that
714 715
             # adjustment here. We don't do this unless needed because it's less
15  docs/db-api.txt
@@ -1137,7 +1137,12 @@ Examples::
1137 1137
 SQL equivalents::
1138 1138
 
1139 1139
     SELECT ... WHERE id = 14;
1140  
-    SELECT ... WHERE id = NULL;
  1140
+    SELECT ... WHERE id IS NULL;
  1141
+
  1142
+**New in Django development version:** The semantics of ``id__exact=None`` have
  1143
+changed in the development version. Previously, it was (intentionally)
  1144
+converted to ``WHERE id = NULL`` at the SQL level, which would never match
  1145
+anything. It has now been changed to behave the same as ``id__isnull=True``.
1141 1146
 
1142 1147
 iexact
1143 1148
 ~~~~~~
@@ -1367,14 +1372,6 @@ SQL equivalent::
1367 1372
 
1368 1373
     SELECT ... WHERE pub_date IS NULL;
1369 1374
 
1370  
-.. admonition:: ``__isnull=True`` vs ``__exact=None``
1371  
-
1372  
-    There is an important difference between ``__isnull=True`` and
1373  
-    ``__exact=None``. ``__exact=None`` will *always* return an empty result
1374  
-    set, because SQL requires that no value is equal to ``NULL``.
1375  
-    ``__isnull`` determines if the field is currently holding the value
1376  
-    of ``NULL`` without performing a comparison.
1377  
-
1378 1375
 search
1379 1376
 ~~~~~~
1380 1377
 
5  tests/modeltests/many_to_one_null/models.py
@@ -80,6 +80,11 @@ def __unicode__(self):
80 80
 >>> Article.objects.filter(reporter__isnull=True)
81 81
 [<Article: Third>]
82 82
 
  83
+# We can achieve the same thing by filtering for the case where the reporter is
  84
+# None.
  85
+>>> Article.objects.filter(reporter=None)
  86
+[<Article: Third>]
  87
+
83 88
 # Set the reporter for the Third article
84 89
 >>> r.article_set.add(a3)
85 90
 >>> r.article_set.all()
7  tests/regressiontests/null_queries/models.py
@@ -24,10 +24,15 @@ def __unicode__(self):
24 24
 >>> c2 = Choice(poll=p1, choice='Why Not?')
25 25
 >>> c2.save()
26 26
 
27  
-# Exact query with value None returns nothing (=NULL in sql)
  27
+# Exact query with value None returns nothing ("is NULL" in sql, but every 'id'
  28
+# field has a value).
28 29
 >>> Choice.objects.filter(id__exact=None)
29 30
 []
30 31
 
  32
+Excluding the previous result returns everything.
  33
+>>> Choice.objects.exclude(id=None).order_by('id')
  34
+[<Choice: Choice: Because. in poll Q: Why? >, <Choice: Choice: Why Not? in poll Q: Why? >]
  35
+
31 36
 # Valid query, but fails because foo isn't a keyword
32 37
 >>> Choice.objects.filter(foo__exact=None)
33 38
 Traceback (most recent call last):

0 notes on commit a241817

Please sign in to comment.
Something went wrong with that request. Please try again.