diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 33acebf0b61..58a84904dc3 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -73,18 +73,16 @@ def validate(cls, model): " associated with a field name." % (cls.__name__, idx, item.__name__)) else: - try: - # Check for option #2 (tuple) - field, list_filter_class = item - except (TypeError, ValueError): - # item is option #1 - field = item - else: + if isinstance(item, (tuple, list)): # item is option #2 + field, list_filter_class = item if not issubclass(list_filter_class, FieldListFilter): raise ImproperlyConfigured("'%s.list_filter[%d][1]'" " is '%s' which is not of type FieldListFilter." % (cls.__name__, idx, list_filter_class.__name__)) + else: + # item is option #1 + field = item # Validate the field string try: get_fields_from_path(model, field) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index d5f401c8477..50d576b8836 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -97,10 +97,10 @@ class ChangeList(object): self.model, self.model_admin) else: field_path = None - try: - # This is custom FieldListFilter class for a given field. + if isinstance(list_filer, (tuple, list)): + # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filer - except (TypeError, ValueError): + else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. diff --git a/tests/regressiontests/admin_filters/models.py b/tests/regressiontests/admin_filters/models.py index 80d54c75a6d..fbf14e87c3a 100644 --- a/tests/regressiontests/admin_filters/models.py +++ b/tests/regressiontests/admin_filters/models.py @@ -8,6 +8,7 @@ class Book(models.Model): contributors = models.ManyToManyField(User, related_name='books_contributed', blank=True, null=True) is_best_seller = models.NullBooleanField(default=0) date_registered = models.DateField(null=True) + no = models.IntegerField(verbose_name=u'number', blank=True, null=True) # This field is intentionally 2 characters long. See #16080. def __unicode__(self): return self.title diff --git a/tests/regressiontests/admin_filters/tests.py b/tests/regressiontests/admin_filters/tests.py index f5542ddcc8b..cccfe340775 100644 --- a/tests/regressiontests/admin_filters/tests.py +++ b/tests/regressiontests/admin_filters/tests.py @@ -66,7 +66,7 @@ class CustomUserAdmin(UserAdmin): list_filter = ('books_authored', 'books_contributed') class BookAdmin(ModelAdmin): - list_filter = ('year', 'author', 'contributors', 'is_best_seller', 'date_registered') + list_filter = ('year', 'author', 'contributors', 'is_best_seller', 'date_registered', 'no') order_by = '-id' class DecadeFilterBookAdmin(ModelAdmin): @@ -100,8 +100,8 @@ class ListFiltersTests(TestCase): # Books self.djangonaut_book = Book.objects.create(title='Djangonaut: an art of living', year=2009, author=self.alfred, is_best_seller=True, date_registered=self.today) - self.bio_book = Book.objects.create(title='Django: a biography', year=1999, author=self.alfred, is_best_seller=False) - self.django_book = Book.objects.create(title='The Django Book', year=None, author=self.bob, is_best_seller=None, date_registered=self.today) + self.bio_book = Book.objects.create(title='Django: a biography', year=1999, author=self.alfred, is_best_seller=False, no=207) + self.django_book = Book.objects.create(title='The Django Book', year=None, author=self.bob, is_best_seller=None, date_registered=self.today, no=103) self.gipsy_book = Book.objects.create(title='Gipsy guitar for dummies', year=2002, is_best_seller=True, date_registered=self.one_week_ago) self.gipsy_book.contributors = [self.bob, self.lisa] self.gipsy_book.save() @@ -528,3 +528,22 @@ class ListFiltersTests(TestCase): self.assertEqual(choices[2]['display'], u'the 2000\'s') self.assertEqual(choices[2]['selected'], False) self.assertEqual(choices[2]['query_string'], '?publication-decade=the+00s') + + def test_two_characters_long_field(self): + """ + Ensure that list_filter works with two-characters long field names. + Refs #16080. + """ + modeladmin = BookAdmin(Book, site) + request = self.request_factory.get('/', {'no': '207'}) + changelist = self.get_changelist(request, Book, modeladmin) + + # Make sure the correct queryset is returned + queryset = changelist.get_query_set(request) + self.assertEqual(list(queryset), [self.bio_book]) + + filterspec = changelist.get_filters(request)[0][-1] + self.assertEqual(force_unicode(filterspec.title), u'number') + choices = list(filterspec.choices(changelist)) + self.assertEqual(choices[2]['selected'], True) + self.assertEqual(choices[2]['query_string'], '?no=207') diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py index 2eda54e7fba..26eaaae07dc 100644 --- a/tests/regressiontests/modeladmin/models.py +++ b/tests/regressiontests/modeladmin/models.py @@ -34,6 +34,7 @@ class ValidationTestModel(models.Model): is_active = models.BooleanField() pub_date = models.DateTimeField() band = models.ForeignKey(Band) + no = models.IntegerField(verbose_name="Number", blank=True, null=True) # This field is intentionally 2 characters long. See #16080. def decade_published_in(self): return self.pub_date.strftime('%Y')[:3] + "0's" diff --git a/tests/regressiontests/modeladmin/tests.py b/tests/regressiontests/modeladmin/tests.py index 530b476b7c5..7387a952581 100644 --- a/tests/regressiontests/modeladmin/tests.py +++ b/tests/regressiontests/modeladmin/tests.py @@ -911,7 +911,7 @@ class ValidationTests(unittest.TestCase): # Valid declarations below ----------- class ValidationTestModelAdmin(ModelAdmin): - list_filter = ('is_active', AwesomeFilter, ('is_active', BooleanFieldListFilter)) + list_filter = ('is_active', AwesomeFilter, ('is_active', BooleanFieldListFilter), 'no') validate(ValidationTestModelAdmin, ValidationTestModel)