Jelajahi Sumber

Fixed #26277 -- Added support for null values in ChoicesFieldListFilter.

Vincenzo Pandolfo 9 tahun lalu
induk
melakukan
069319396f

+ 21 - 3
django/contrib/admin/filters.py

@@ -269,25 +269,43 @@ FieldListFilter.register(
 class ChoicesFieldListFilter(FieldListFilter):
     def __init__(self, field, request, params, model, model_admin, field_path):
         self.lookup_kwarg = '%s__exact' % field_path
+        self.lookup_kwarg_isnull = '%s__isnull' % field_path
         self.lookup_val = request.GET.get(self.lookup_kwarg)
+        self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull)
         super(ChoicesFieldListFilter, self).__init__(
             field, request, params, model, model_admin, field_path)
 
     def expected_parameters(self):
-        return [self.lookup_kwarg]
+        return [self.lookup_kwarg, self.lookup_kwarg_isnull]
 
     def choices(self, changelist):
         yield {
             'selected': self.lookup_val is None,
-            'query_string': changelist.get_query_string({}, [self.lookup_kwarg]),
+            'query_string': changelist.get_query_string(
+                {}, [self.lookup_kwarg, self.lookup_kwarg_isnull]
+            ),
             'display': _('All')
         }
+        none_title = ''
         for lookup, title in self.field.flatchoices:
+            if lookup is None:
+                none_title = title
+                continue
             yield {
                 'selected': smart_text(lookup) == self.lookup_val,
-                'query_string': changelist.get_query_string({self.lookup_kwarg: lookup}),
+                'query_string': changelist.get_query_string(
+                    {self.lookup_kwarg: lookup}, [self.lookup_kwarg_isnull]
+                ),
                 'display': title,
             }
+        if none_title:
+            yield {
+                'selected': bool(self.lookup_val_isnull),
+                'query_string': changelist.get_query_string({
+                    self.lookup_kwarg_isnull: 'True',
+                }, [self.lookup_kwarg]),
+                'display': none_title,
+            }
 
 FieldListFilter.register(lambda f: bool(f.choices), ChoicesFieldListFilter)
 

+ 7 - 0
tests/admin_filters/models.py

@@ -75,5 +75,12 @@ class Bookmark(models.Model):
     url = models.URLField()
     tags = GenericRelation(TaggedItem)
 
+    CHOICES = [
+        ('a', 'A'),
+        (None, 'None'),
+        ('', '-'),
+    ]
+    none_or_null = models.CharField(max_length=20, choices=CHOICES, blank=True, null=True)
+
     def __str__(self):
         return self.url

+ 16 - 0
tests/admin_filters/tests.py

@@ -302,6 +302,22 @@ class ListFiltersTests(TestCase):
             modeladmin.list_max_show_all, modeladmin.list_editable, modeladmin,
         )
 
+    def test_choicesfieldlistfilter_has_none_choice(self):
+        """
+        The last choice is for the None value.
+        """
+        class BookmarkChoicesAdmin(ModelAdmin):
+            list_display = ['none_or_null']
+            list_filter = ['none_or_null']
+
+        modeladmin = BookmarkChoicesAdmin(Bookmark, site)
+        request = self.request_factory.get('/', {})
+        changelist = self.get_changelist(request, Bookmark, modeladmin)
+        filterspec = changelist.get_filters(request)[0][0]
+        choices = list(filterspec.choices(changelist))
+        self.assertEqual(choices[-1]['display'], 'None')
+        self.assertEqual(choices[-1]['query_string'], '?none_or_null__isnull=True')
+
     def test_datefieldlistfilter(self):
         modeladmin = BookAdmin(Book, site)