from __future__ import unicode_literals import datetime from django.contrib.admin import (site, ModelAdmin, SimpleListFilter, BooleanFieldListFilter, AllValuesFieldListFilter, RelatedOnlyFieldListFilter) from django.contrib.admin.views.main import ChangeList from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, RequestFactory, override_settings from django.utils.encoding import force_text from django.utils import six from .models import Book, Department, Employee, Bookmark, TaggedItem def select_by(dictlist, key, value): return [x for x in dictlist if x[key] == value][0] class DecadeListFilter(SimpleListFilter): def lookups(self, request, model_admin): return ( ('the 80s', "the 1980's"), ('the 90s', "the 1990's"), ('the 00s', "the 2000's"), ('other', "other decades"), ) def queryset(self, request, queryset): decade = self.value() if decade == 'the 80s': return queryset.filter(year__gte=1980, year__lte=1989) if decade == 'the 90s': return queryset.filter(year__gte=1990, year__lte=1999) if decade == 'the 00s': return queryset.filter(year__gte=2000, year__lte=2009) class DecadeListFilterWithTitleAndParameter(DecadeListFilter): title = 'publication decade' parameter_name = 'publication-decade' class DecadeListFilterWithoutTitle(DecadeListFilter): parameter_name = 'publication-decade' class DecadeListFilterWithoutParameter(DecadeListFilter): title = 'publication decade' class DecadeListFilterWithNoneReturningLookups(DecadeListFilterWithTitleAndParameter): def lookups(self, request, model_admin): pass class DecadeListFilterWithFailingQueryset(DecadeListFilterWithTitleAndParameter): def queryset(self, request, queryset): raise 1 / 0 class DecadeListFilterWithQuerysetBasedLookups(DecadeListFilterWithTitleAndParameter): def lookups(self, request, model_admin): qs = model_admin.get_queryset(request) if qs.filter(year__gte=1980, year__lte=1989).exists(): yield ('the 80s', "the 1980's") if qs.filter(year__gte=1990, year__lte=1999).exists(): yield ('the 90s', "the 1990's") if qs.filter(year__gte=2000, year__lte=2009).exists(): yield ('the 00s', "the 2000's") class DecadeListFilterParameterEndsWith__In(DecadeListFilter): title = 'publication decade' parameter_name = 'decade__in' # Ends with '__in" class DecadeListFilterParameterEndsWith__Isnull(DecadeListFilter): title = 'publication decade' parameter_name = 'decade__isnull' # Ends with '__isnull" class DepartmentListFilterLookupWithNonStringValue(SimpleListFilter): title = 'department' parameter_name = 'department' def lookups(self, request, model_admin): return sorted({ (employee.department.id, # Intentionally not a string (Refs #19318) employee.department.code) for employee in model_admin.get_queryset(request).all() }) def queryset(self, request, queryset): if self.value(): return queryset.filter(department__id=self.value()) class DepartmentListFilterLookupWithUnderscoredParameter(DepartmentListFilterLookupWithNonStringValue): parameter_name = 'department__whatever' class DepartmentListFilterLookupWithDynamicValue(DecadeListFilterWithTitleAndParameter): def lookups(self, request, model_admin): if self.value() == 'the 80s': return (('the 90s', "the 1990's"),) elif self.value() == 'the 90s': return (('the 80s', "the 1980's"),) else: return (('the 80s', "the 1980's"), ('the 90s', "the 1990's"),) class CustomUserAdmin(UserAdmin): list_filter = ('books_authored', 'books_contributed') class BookAdmin(ModelAdmin): list_filter = ('year', 'author', 'contributors', 'is_best_seller', 'date_registered', 'no') ordering = ('-id',) class BookAdminWithTupleBooleanFilter(BookAdmin): list_filter = ('year', 'author', 'contributors', ('is_best_seller', BooleanFieldListFilter), 'date_registered', 'no') class BookAdminWithUnderscoreLookupAndTuple(BookAdmin): list_filter = ('year', ('author__email', AllValuesFieldListFilter), 'contributors', 'is_best_seller', 'date_registered', 'no') class BookAdminWithCustomQueryset(ModelAdmin): def __init__(self, user, *args, **kwargs): self.user = user super(BookAdminWithCustomQueryset, self).__init__(*args, **kwargs) list_filter = ('year',) def get_queryset(self, request): return super(BookAdminWithCustomQueryset, self).get_queryset(request).filter(author=self.user) class BookAdminRelatedOnlyFilter(ModelAdmin): list_filter = ( 'year', 'is_best_seller', 'date_registered', 'no', ('author', RelatedOnlyFieldListFilter), ('contributors', RelatedOnlyFieldListFilter), ) ordering = ('-id',) class DecadeFilterBookAdmin(ModelAdmin): list_filter = ('author', DecadeListFilterWithTitleAndParameter) ordering = ('-id',) class DecadeFilterBookAdminWithoutTitle(ModelAdmin): list_filter = (DecadeListFilterWithoutTitle,) class DecadeFilterBookAdminWithoutParameter(ModelAdmin): list_filter = (DecadeListFilterWithoutParameter,) class DecadeFilterBookAdminWithNoneReturningLookups(ModelAdmin): list_filter = (DecadeListFilterWithNoneReturningLookups,) class DecadeFilterBookAdminWithFailingQueryset(ModelAdmin): list_filter = (DecadeListFilterWithFailingQueryset,) class DecadeFilterBookAdminWithQuerysetBasedLookups(ModelAdmin): list_filter = (DecadeListFilterWithQuerysetBasedLookups,) class DecadeFilterBookAdminParameterEndsWith__In(ModelAdmin): list_filter = (DecadeListFilterParameterEndsWith__In,) class DecadeFilterBookAdminParameterEndsWith__Isnull(ModelAdmin): list_filter = (DecadeListFilterParameterEndsWith__Isnull,) class EmployeeAdmin(ModelAdmin): list_display = ['name', 'department'] list_filter = ['department'] class DepartmentFilterEmployeeAdmin(EmployeeAdmin): list_filter = [DepartmentListFilterLookupWithNonStringValue, ] class DepartmentFilterUnderscoredEmployeeAdmin(EmployeeAdmin): list_filter = [DepartmentListFilterLookupWithUnderscoredParameter, ] class DepartmentFilterDynamicValueBookAdmin(EmployeeAdmin): list_filter = [DepartmentListFilterLookupWithDynamicValue, ] class BookmarkAdminGenericRelation(ModelAdmin): list_filter = ['tags__tag'] class ListFiltersTests(TestCase): def setUp(self): self.today = datetime.date.today() self.tomorrow = self.today + datetime.timedelta(days=1) self.one_week_ago = self.today - datetime.timedelta(days=7) if self.today.month == 12: self.next_month = self.today.replace(year=self.today.year + 1, month=1, day=1) else: self.next_month = self.today.replace(month=self.today.month + 1, day=1) self.next_year = self.today.replace(year=self.today.year + 1, month=1, day=1) self.request_factory = RequestFactory() # Users self.alfred = User.objects.create_user('alfred', 'alfred@example.com') self.bob = User.objects.create_user('bob', 'bob@example.com') self.lisa = User.objects.create_user('lisa', 'lisa@example.com') # 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, 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() # Departments self.dev = Department.objects.create(code='DEV', description='Development') self.design = Department.objects.create(code='DSN', description='Design') # Employees self.john = Employee.objects.create(name='John Blue', department=self.dev) self.jack = Employee.objects.create(name='Jack Red', department=self.design) def get_changelist(self, request, model, modeladmin): return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links, modeladmin.list_filter, modeladmin.date_hierarchy, modeladmin.search_fields, modeladmin.list_select_related, modeladmin.list_per_page, modeladmin.list_max_show_all, modeladmin.list_editable, modeladmin) def test_datefieldlistfilter(self): modeladmin = BookAdmin(Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) request = self.request_factory.get('/', {'date_registered__gte': self.today, 'date_registered__lt': self.tomorrow}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.django_book, self.djangonaut_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][4] self.assertEqual(force_text(filterspec.title), 'date registered') choice = select_by(filterspec.choices(changelist), "display", "Today") self.assertEqual(choice['selected'], True) self.assertEqual( choice['query_string'], '?date_registered__gte=%s&date_registered__lt=%s' % ( self.today, self.tomorrow, ) ) request = self.request_factory.get('/', {'date_registered__gte': self.today.replace(day=1), 'date_registered__lt': self.next_month}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) if (self.today.year, self.today.month) == (self.one_week_ago.year, self.one_week_ago.month): # In case one week ago is in the same month. self.assertEqual(list(queryset), [self.gipsy_book, self.django_book, self.djangonaut_book]) else: self.assertEqual(list(queryset), [self.django_book, self.djangonaut_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][4] self.assertEqual(force_text(filterspec.title), 'date registered') choice = select_by(filterspec.choices(changelist), "display", "This month") self.assertEqual(choice['selected'], True) self.assertEqual( choice['query_string'], '?date_registered__gte=%s&date_registered__lt=%s' % ( self.today.replace(day=1), self.next_month, ) ) request = self.request_factory.get('/', {'date_registered__gte': self.today.replace(month=1, day=1), 'date_registered__lt': self.next_year}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) if self.today.year == self.one_week_ago.year: # In case one week ago is in the same year. self.assertEqual(list(queryset), [self.gipsy_book, self.django_book, self.djangonaut_book]) else: self.assertEqual(list(queryset), [self.django_book, self.djangonaut_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][4] self.assertEqual(force_text(filterspec.title), 'date registered') choice = select_by(filterspec.choices(changelist), "display", "This year") self.assertEqual(choice['selected'], True) self.assertEqual( choice['query_string'], '?date_registered__gte=%s&date_registered__lt=%s' % ( self.today.replace(month=1, day=1), self.next_year, ) ) request = self.request_factory.get('/', { 'date_registered__gte': str(self.one_week_ago), 'date_registered__lt': str(self.tomorrow), }) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.gipsy_book, self.django_book, self.djangonaut_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][4] self.assertEqual(force_text(filterspec.title), 'date registered') choice = select_by(filterspec.choices(changelist), "display", "Past 7 days") self.assertEqual(choice['selected'], True) self.assertEqual( choice['query_string'], '?date_registered__gte=%s&date_registered__lt=%s' % ( str(self.one_week_ago), str(self.tomorrow), ) ) @override_settings(USE_TZ=True) def test_datefieldlistfilter_with_time_zone_support(self): # Regression for #17830 self.test_datefieldlistfilter() def test_allvaluesfieldlistfilter(self): modeladmin = BookAdmin(Book, site) request = self.request_factory.get('/', {'year__isnull': 'True'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.django_book]) # Make sure the last choice is None and is selected filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'year') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[-1]['selected'], True) self.assertEqual(choices[-1]['query_string'], '?year__isnull=True') request = self.request_factory.get('/', {'year': '2002'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'year') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[2]['selected'], True) self.assertEqual(choices[2]['query_string'], '?year=2002') def test_allvaluesfieldlistfilter_custom_qs(self): # Make sure that correct filters are returned with custom querysets modeladmin = BookAdminWithCustomQueryset(self.alfred, Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) filterspec = changelist.get_filters(request)[0][0] choices = list(filterspec.choices(changelist)) # Should have 'All', 1999 and 2009 options i.e. the subset of years of # books written by alfred (which is the filtering criteria set by # BookAdminWithCustomQueryset.get_queryset()) self.assertEqual(3, len(choices)) self.assertEqual(choices[0]['query_string'], '?') self.assertEqual(choices[1]['query_string'], '?year=1999') self.assertEqual(choices[2]['query_string'], '?year=2009') def test_relatedfieldlistfilter_foreignkey(self): modeladmin = BookAdmin(Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) # Make sure that all users are present in the author's list filter filterspec = changelist.get_filters(request)[0][1] expected = [(self.alfred.pk, 'alfred'), (self.bob.pk, 'bob'), (self.lisa.pk, 'lisa')] self.assertEqual(sorted(filterspec.lookup_choices), sorted(expected)) request = self.request_factory.get('/', {'author__isnull': 'True'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.gipsy_book]) # Make sure the last choice is None and is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'Verbose Author') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[-1]['selected'], True) self.assertEqual(choices[-1]['query_string'], '?author__isnull=True') request = self.request_factory.get('/', {'author__id__exact': self.alfred.pk}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'Verbose Author') # order of choices depends on User model, which has no order choice = select_by(filterspec.choices(changelist), "display", "alfred") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?author__id__exact=%d' % self.alfred.pk) def test_relatedfieldlistfilter_manytomany(self): modeladmin = BookAdmin(Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) # Make sure that all users are present in the contrib's list filter filterspec = changelist.get_filters(request)[0][2] expected = [(self.alfred.pk, 'alfred'), (self.bob.pk, 'bob'), (self.lisa.pk, 'lisa')] self.assertEqual(sorted(filterspec.lookup_choices), sorted(expected)) request = self.request_factory.get('/', {'contributors__isnull': 'True'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.django_book, self.bio_book, self.djangonaut_book]) # Make sure the last choice is None and is selected filterspec = changelist.get_filters(request)[0][2] self.assertEqual(force_text(filterspec.title), 'Verbose Contributors') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[-1]['selected'], True) self.assertEqual(choices[-1]['query_string'], '?contributors__isnull=True') request = self.request_factory.get('/', {'contributors__id__exact': self.bob.pk}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][2] self.assertEqual(force_text(filterspec.title), 'Verbose Contributors') choice = select_by(filterspec.choices(changelist), "display", "bob") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?contributors__id__exact=%d' % self.bob.pk) def test_relatedfieldlistfilter_reverse_relationships(self): modeladmin = CustomUserAdmin(User, site) # FK relationship ----- request = self.request_factory.get('/', {'books_authored__isnull': 'True'}) changelist = self.get_changelist(request, User, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.lisa]) # Make sure the last choice is None and is selected filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'book') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[-1]['selected'], True) self.assertEqual(choices[-1]['query_string'], '?books_authored__isnull=True') request = self.request_factory.get('/', {'books_authored__id__exact': self.bio_book.pk}) changelist = self.get_changelist(request, User, modeladmin) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'book') choice = select_by(filterspec.choices(changelist), "display", self.bio_book.title) self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?books_authored__id__exact=%d' % self.bio_book.pk) # M2M relationship ----- request = self.request_factory.get('/', {'books_contributed__isnull': 'True'}) changelist = self.get_changelist(request, User, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.alfred]) # Make sure the last choice is None and is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'book') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[-1]['selected'], True) self.assertEqual(choices[-1]['query_string'], '?books_contributed__isnull=True') request = self.request_factory.get('/', {'books_contributed__id__exact': self.django_book.pk}) changelist = self.get_changelist(request, User, modeladmin) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'book') choice = select_by(filterspec.choices(changelist), "display", self.django_book.title) self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?books_contributed__id__exact=%d' % self.django_book.pk) def test_relatedonlyfieldlistfilter_foreignkey(self): modeladmin = BookAdminRelatedOnlyFilter(Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) # Make sure that only actual authors are present in author's list filter filterspec = changelist.get_filters(request)[0][4] expected = [(self.alfred.pk, 'alfred'), (self.bob.pk, 'bob')] self.assertEqual(sorted(filterspec.lookup_choices), sorted(expected)) def test_relatedonlyfieldlistfilter_manytomany(self): modeladmin = BookAdminRelatedOnlyFilter(Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) # Make sure that only actual contributors are present in contrib's list filter filterspec = changelist.get_filters(request)[0][5] expected = [(self.bob.pk, 'bob'), (self.lisa.pk, 'lisa')] self.assertEqual(sorted(filterspec.lookup_choices), sorted(expected)) def test_listfilter_genericrelation(self): django_bookmark = Bookmark.objects.create(url='https://www.djangoproject.com/') python_bookmark = Bookmark.objects.create(url='https://www.python.org/') kernel_bookmark = Bookmark.objects.create(url='https://www.kernel.org/') TaggedItem.objects.create(content_object=django_bookmark, tag='python') TaggedItem.objects.create(content_object=python_bookmark, tag='python') TaggedItem.objects.create(content_object=kernel_bookmark, tag='linux') modeladmin = BookmarkAdminGenericRelation(Bookmark, site) request = self.request_factory.get('/', {'tags__tag': 'python'}) changelist = self.get_changelist(request, Bookmark, modeladmin) queryset = changelist.get_queryset(request) expected = [python_bookmark, django_bookmark] self.assertEqual(list(queryset), expected) def test_booleanfieldlistfilter(self): modeladmin = BookAdmin(Book, site) self.verify_booleanfieldlistfilter(modeladmin) def test_booleanfieldlistfilter_tuple(self): modeladmin = BookAdminWithTupleBooleanFilter(Book, site) self.verify_booleanfieldlistfilter(modeladmin) def verify_booleanfieldlistfilter(self, modeladmin): request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) request = self.request_factory.get('/', {'is_best_seller__exact': 0}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.bio_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][3] self.assertEqual(force_text(filterspec.title), 'is best seller') choice = select_by(filterspec.choices(changelist), "display", "No") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?is_best_seller__exact=0') request = self.request_factory.get('/', {'is_best_seller__exact': 1}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.gipsy_book, self.djangonaut_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][3] self.assertEqual(force_text(filterspec.title), 'is best seller') choice = select_by(filterspec.choices(changelist), "display", "Yes") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?is_best_seller__exact=1') request = self.request_factory.get('/', {'is_best_seller__isnull': 'True'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.django_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][3] self.assertEqual(force_text(filterspec.title), 'is best seller') choice = select_by(filterspec.choices(changelist), "display", "Unknown") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?is_best_seller__isnull=True') def test_fieldlistfilter_underscorelookup_tuple(self): """ Ensure ('fieldpath', ClassName ) lookups pass lookup_allowed checks when fieldpath contains double underscore in value. Refs #19182 """ modeladmin = BookAdminWithUnderscoreLookupAndTuple(Book, site) request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) request = self.request_factory.get('/', {'author__email': 'alfred@example.com'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.bio_book, self.djangonaut_book]) def test_simplelistfilter(self): modeladmin = DecadeFilterBookAdmin(Book, site) # Make sure that the first option is 'All' --------------------------- request = self.request_factory.get('/', {}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), list(Book.objects.all().order_by('-id'))) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[0]['display'], 'All') self.assertEqual(choices[0]['selected'], True) self.assertEqual(choices[0]['query_string'], '?') # Look for books in the 1980s ---------------------------------------- request = self.request_factory.get('/', {'publication-decade': 'the 80s'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), []) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[1]['display'], 'the 1980\'s') self.assertEqual(choices[1]['selected'], True) self.assertEqual(choices[1]['query_string'], '?publication-decade=the+80s') # Look for books in the 1990s ---------------------------------------- request = self.request_factory.get('/', {'publication-decade': 'the 90s'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.bio_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[2]['display'], 'the 1990\'s') self.assertEqual(choices[2]['selected'], True) self.assertEqual(choices[2]['query_string'], '?publication-decade=the+90s') # Look for books in the 2000s ---------------------------------------- request = self.request_factory.get('/', {'publication-decade': 'the 00s'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.gipsy_book, self.djangonaut_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[3]['display'], 'the 2000\'s') self.assertEqual(choices[3]['selected'], True) self.assertEqual(choices[3]['query_string'], '?publication-decade=the+00s') # Combine multiple filters ------------------------------------------- request = self.request_factory.get('/', {'publication-decade': 'the 00s', 'author__id__exact': self.alfred.pk}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.djangonaut_book]) # Make sure the correct choices are selected filterspec = changelist.get_filters(request)[0][1] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[3]['display'], 'the 2000\'s') self.assertEqual(choices[3]['selected'], True) self.assertEqual(choices[3]['query_string'], '?author__id__exact=%s&publication-decade=the+00s' % self.alfred.pk) filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'Verbose Author') choice = select_by(filterspec.choices(changelist), "display", "alfred") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?author__id__exact=%s&publication-decade=the+00s' % self.alfred.pk) def test_listfilter_without_title(self): """ Any filter must define a title. """ modeladmin = DecadeFilterBookAdminWithoutTitle(Book, site) request = self.request_factory.get('/', {}) six.assertRaisesRegex(self, ImproperlyConfigured, "The list filter 'DecadeListFilterWithoutTitle' does not specify a 'title'.", self.get_changelist, request, Book, modeladmin) def test_simplelistfilter_without_parameter(self): """ Any SimpleListFilter must define a parameter_name. """ modeladmin = DecadeFilterBookAdminWithoutParameter(Book, site) request = self.request_factory.get('/', {}) six.assertRaisesRegex(self, ImproperlyConfigured, "The list filter 'DecadeListFilterWithoutParameter' does not specify a 'parameter_name'.", self.get_changelist, request, Book, modeladmin) def test_simplelistfilter_with_none_returning_lookups(self): """ A SimpleListFilter lookups method can return None but disables the filter completely. """ modeladmin = DecadeFilterBookAdminWithNoneReturningLookups(Book, site) request = self.request_factory.get('/', {}) changelist = self.get_changelist(request, Book, modeladmin) filterspec = changelist.get_filters(request)[0] self.assertEqual(len(filterspec), 0) def test_filter_with_failing_queryset(self): """ Ensure that when a filter's queryset method fails, it fails loudly and the corresponding exception doesn't get swallowed. Refs #17828. """ modeladmin = DecadeFilterBookAdminWithFailingQueryset(Book, site) request = self.request_factory.get('/', {}) self.assertRaises(ZeroDivisionError, self.get_changelist, request, Book, modeladmin) def test_simplelistfilter_with_queryset_based_lookups(self): modeladmin = DecadeFilterBookAdminWithQuerysetBasedLookups(Book, site) request = self.request_factory.get('/', {}) changelist = self.get_changelist(request, Book, modeladmin) filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(len(choices), 3) self.assertEqual(choices[0]['display'], 'All') self.assertEqual(choices[0]['selected'], True) self.assertEqual(choices[0]['query_string'], '?') self.assertEqual(choices[1]['display'], 'the 1990\'s') self.assertEqual(choices[1]['selected'], False) self.assertEqual(choices[1]['query_string'], '?publication-decade=the+90s') self.assertEqual(choices[2]['display'], '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_queryset(request) self.assertEqual(list(queryset), [self.bio_book]) filterspec = changelist.get_filters(request)[0][-1] self.assertEqual(force_text(filterspec.title), 'number') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[2]['selected'], True) self.assertEqual(choices[2]['query_string'], '?no=207') def test_parameter_ends_with__in__or__isnull(self): """ Ensure that a SimpleListFilter's parameter name is not mistaken for a model field if it ends with '__isnull' or '__in'. Refs #17091. """ # When it ends with '__in' ----------------------------------------- modeladmin = DecadeFilterBookAdminParameterEndsWith__In(Book, site) request = self.request_factory.get('/', {'decade__in': 'the 90s'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.bio_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[2]['display'], 'the 1990\'s') self.assertEqual(choices[2]['selected'], True) self.assertEqual(choices[2]['query_string'], '?decade__in=the+90s') # When it ends with '__isnull' --------------------------------------- modeladmin = DecadeFilterBookAdminParameterEndsWith__Isnull(Book, site) request = self.request_factory.get('/', {'decade__isnull': 'the 90s'}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.bio_book]) # Make sure the correct choice is selected filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[2]['display'], 'the 1990\'s') self.assertEqual(choices[2]['selected'], True) self.assertEqual(choices[2]['query_string'], '?decade__isnull=the+90s') def test_lookup_with_non_string_value(self): """ Ensure choices are set the selected class when using non-string values for lookups in SimpleListFilters. Refs #19318 """ modeladmin = DepartmentFilterEmployeeAdmin(Employee, site) request = self.request_factory.get('/', {'department': self.john.pk}) changelist = self.get_changelist(request, Employee, modeladmin) queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.john]) filterspec = changelist.get_filters(request)[0][-1] self.assertEqual(force_text(filterspec.title), 'department') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[1]['display'], 'DEV') self.assertEqual(choices[1]['selected'], True) self.assertEqual(choices[1]['query_string'], '?department=%s' % self.john.pk) def test_lookup_with_non_string_value_underscored(self): """ Ensure SimpleListFilter lookups pass lookup_allowed checks when parameter_name attribute contains double-underscore value. Refs #19182 """ modeladmin = DepartmentFilterUnderscoredEmployeeAdmin(Employee, site) request = self.request_factory.get('/', {'department__whatever': self.john.pk}) changelist = self.get_changelist(request, Employee, modeladmin) queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.john]) filterspec = changelist.get_filters(request)[0][-1] self.assertEqual(force_text(filterspec.title), 'department') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[1]['display'], 'DEV') self.assertEqual(choices[1]['selected'], True) self.assertEqual(choices[1]['query_string'], '?department__whatever=%s' % self.john.pk) def test_fk_with_to_field(self): """ Ensure that a filter on a FK respects the FK's to_field attribute. Refs #17972. """ modeladmin = EmployeeAdmin(Employee, site) request = self.request_factory.get('/', {}) changelist = self.get_changelist(request, Employee, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.jack, self.john]) filterspec = changelist.get_filters(request)[0][-1] self.assertEqual(force_text(filterspec.title), 'department') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[0]['display'], 'All') self.assertEqual(choices[0]['selected'], True) self.assertEqual(choices[0]['query_string'], '?') self.assertEqual(choices[1]['display'], 'Development') self.assertEqual(choices[1]['selected'], False) self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV') self.assertEqual(choices[2]['display'], 'Design') self.assertEqual(choices[2]['selected'], False) self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN') # Filter by Department=='Development' -------------------------------- request = self.request_factory.get('/', {'department__code__exact': 'DEV'}) changelist = self.get_changelist(request, Employee, modeladmin) # Make sure the correct queryset is returned queryset = changelist.get_queryset(request) self.assertEqual(list(queryset), [self.john]) filterspec = changelist.get_filters(request)[0][-1] self.assertEqual(force_text(filterspec.title), 'department') choices = list(filterspec.choices(changelist)) self.assertEqual(choices[0]['display'], 'All') self.assertEqual(choices[0]['selected'], False) self.assertEqual(choices[0]['query_string'], '?') self.assertEqual(choices[1]['display'], 'Development') self.assertEqual(choices[1]['selected'], True) self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV') self.assertEqual(choices[2]['display'], 'Design') self.assertEqual(choices[2]['selected'], False) self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN') def test_lookup_with_dynamic_value(self): """ Ensure SimpleListFilter can access self.value() inside the lookup. """ modeladmin = DepartmentFilterDynamicValueBookAdmin(Book, site) def _test_choices(request, expected_displays): changelist = self.get_changelist(request, Book, modeladmin) filterspec = changelist.get_filters(request)[0][0] self.assertEqual(force_text(filterspec.title), 'publication decade') choices = tuple(c['display'] for c in filterspec.choices(changelist)) self.assertEqual(choices, expected_displays) _test_choices(self.request_factory.get('/', {}), ("All", "the 1980's", "the 1990's")) _test_choices(self.request_factory.get('/', {'publication-decade': 'the 80s'}), ("All", "the 1990's")) _test_choices(self.request_factory.get('/', {'publication-decade': 'the 90s'}), ("All", "the 1980's"))