Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #20182 - admin lookup should treat 0 as False for __isnull

Thanks Benjie Chen.
  • Loading branch information...
commit 0268aba96bf91de3d172cd6ce2e44c3ce33ef7a0 1 parent d194714
Tim Graham authored
4  django/contrib/admin/util.py
@@ -37,9 +37,9 @@ def prepare_lookup_value(key, value):
37 37
     # if key ends with __in, split parameter into separate values
38 38
     if key.endswith('__in'):
39 39
         value = value.split(',')
40  
-    # if key ends with __isnull, special case '' and false
  40
+    # if key ends with __isnull, special case '' and the string literals 'false' and '0'
41 41
     if key.endswith('__isnull'):
42  
-        if value.lower() in ('', 'false'):
  42
+        if value.lower() in ('', 'false', '0'):
43 43
             value = False
44 44
         else:
45 45
             value = True
2  tests/admin_views/admin.py
@@ -149,7 +149,7 @@ class InquisitionAdmin(admin.ModelAdmin):
149 149
 
150 150
 
151 151
 class SketchAdmin(admin.ModelAdmin):
152  
-    raw_id_fields = ('inquisition',)
  152
+    raw_id_fields = ('inquisition', 'defendant0', 'defendant1')
153 153
 
154 154
 
155 155
 class FabricAdmin(admin.ModelAdmin):
3  tests/admin_views/models.py
@@ -137,6 +137,7 @@ def __str__(self):
137 137
 class Actor(models.Model):
138 138
     name = models.CharField(max_length=50)
139 139
     age = models.IntegerField()
  140
+    title = models.CharField(max_length=50, null=True)
140 141
     def __str__(self):
141 142
         return self.name
142 143
 
@@ -158,6 +159,8 @@ class Sketch(models.Model):
158 159
                                                                    'leader__age': 27,
159 160
                                                                    'expected': False,
160 161
                                                                    })
  162
+    defendant0 = models.ForeignKey(Actor, limit_choices_to={'title__isnull': False}, related_name='as_defendant0')
  163
+    defendant1 = models.ForeignKey(Actor, limit_choices_to={'title__isnull': True}, related_name='as_defendant1')
161 164
 
162 165
     def __str__(self):
163 166
         return self.title
53  tests/admin_views/tests.py
@@ -468,8 +468,12 @@ def testIsNullLookups(self):
468 468
         self.assertContains(response, '4 articles')
469 469
         response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': 'false'})
470 470
         self.assertContains(response, '3 articles')
  471
+        response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': '0'})
  472
+        self.assertContains(response, '3 articles')
471 473
         response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': 'true'})
472 474
         self.assertContains(response, '1 article')
  475
+        response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': '1'})
  476
+        self.assertContains(response, '1 article')
473 477
 
474 478
     def testLogoutAndPasswordChangeURLs(self):
475 479
         response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
@@ -3508,7 +3512,6 @@ def tearDown(self):
3508 3512
 
3509 3513
     def test_limit_choices_to(self):
3510 3514
         """Regression test for 14880"""
3511  
-        # This includes tests integers, strings and booleans in the lookup query string
3512 3515
         actor = Actor.objects.create(name="Palin", age=27)
3513 3516
         inquisition1 = Inquisition.objects.create(expected=True,
3514 3517
                                                   leader=actor,
@@ -3524,11 +3527,57 @@ def test_limit_choices_to(self):
3524 3527
 
3525 3528
         # Handle relative links
3526 3529
         popup_url = urljoin(response.request['PATH_INFO'], popup_url)
3527  
-        # Get the popup
  3530
+        # Get the popup and verify the correct objects show up in the resulting
  3531
+        # page. This step also tests integers, strings and booleans in the
  3532
+        # lookup query string; in model we define inquisition field to have a
  3533
+        # limit_choices_to option that includes a filter on a string field
  3534
+        # (inquisition__actor__name), a filter on an integer field
  3535
+        # (inquisition__actor__age), and a filter on a boolean field
  3536
+        # (inquisition__expected).
3528 3537
         response2 = self.client.get(popup_url)
3529 3538
         self.assertContains(response2, "Spain")
3530 3539
         self.assertNotContains(response2, "England")
3531 3540
 
  3541
+    def test_limit_choices_to_isnull_false(self):
  3542
+        """Regression test for 20182"""
  3543
+        Actor.objects.create(name="Palin", age=27)
  3544
+        Actor.objects.create(name="Kilbraken", age=50, title="Judge")
  3545
+        response = self.client.get('/test_admin/admin/admin_views/sketch/add/')
  3546
+        # Find the link
  3547
+        m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_defendant0"', response.content)
  3548
+        self.assertTrue(m)  # Got a match
  3549
+        popup_url = m.groups()[0].decode().replace("&amp;", "&")
  3550
+
  3551
+        # Handle relative links
  3552
+        popup_url = urljoin(response.request['PATH_INFO'], popup_url)
  3553
+        # Get the popup and verify the correct objects show up in the resulting
  3554
+        # page. This step tests field__isnull=0 gets parsed correctly from the
  3555
+        # lookup query string; in model we define defendant0 field to have a
  3556
+        # limit_choices_to option that includes "actor__title__isnull=False".
  3557
+        response2 = self.client.get(popup_url)
  3558
+        self.assertContains(response2, "Kilbraken")
  3559
+        self.assertNotContains(response2, "Palin")
  3560
+
  3561
+    def test_limit_choices_to_isnull_true(self):
  3562
+        """Regression test for 20182"""
  3563
+        Actor.objects.create(name="Palin", age=27)
  3564
+        Actor.objects.create(name="Kilbraken", age=50, title="Judge")
  3565
+        response = self.client.get('/test_admin/admin/admin_views/sketch/add/')
  3566
+        # Find the link
  3567
+        m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_defendant1"', response.content)
  3568
+        self.assertTrue(m)  # Got a match
  3569
+        popup_url = m.groups()[0].decode().replace("&amp;", "&")
  3570
+
  3571
+        # Handle relative links
  3572
+        popup_url = urljoin(response.request['PATH_INFO'], popup_url)
  3573
+        # Get the popup and verify the correct objects show up in the resulting
  3574
+        # page. This step tests field__isnull=1 gets parsed correctly from the
  3575
+        # lookup query string; in model we define defendant1 field to have a
  3576
+        # limit_choices_to option that includes "actor__title__isnull=True".
  3577
+        response2 = self.client.get(popup_url)
  3578
+        self.assertNotContains(response2, "Kilbraken")
  3579
+        self.assertContains(response2, "Palin")
  3580
+
3532 3581
 
3533 3582
 @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
3534 3583
 class UserAdminTest(TestCase):

0 notes on commit 0268aba

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