12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106 |
- from __future__ import unicode_literals
- import datetime
- import sys
- import unittest
- from django.contrib.admin import (
- AllValuesFieldListFilter, BooleanFieldListFilter, ModelAdmin,
- RelatedOnlyFieldListFilter, SimpleListFilter, site,
- )
- 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 RequestFactory, TestCase, override_settings
- from django.utils.encoding import force_text
- from .models import Book, Bookmark, Department, Employee, 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 NotNinetiesListFilter(SimpleListFilter):
- title = "Not nineties books"
- parameter_name = "book_year"
- def lookups(self, request, model_admin):
- return (
- ('the 90s', "the 1990's"),
- )
- def queryset(self, request, queryset):
- if self.value() == 'the 90s':
- return queryset.filter(year__gte=1990, year__lte=1999)
- else:
- return queryset.exclude(year__gte=1990, year__lte=1999)
- 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),
- ('employee__department', RelatedOnlyFieldListFilter),
- )
- ordering = ('-id',)
- class DecadeFilterBookAdmin(ModelAdmin):
- list_filter = ('author', DecadeListFilterWithTitleAndParameter)
- ordering = ('-id',)
- class NotNinetiesListFilterAdmin(ModelAdmin):
- list_filter = (NotNinetiesListFilter,)
- 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.guitar_book = Book.objects.create(
- title='Guitar for dummies', year=2002, is_best_seller=True,
- date_registered=self.one_week_ago,
- )
- self.guitar_book.contributors.set([self.bob, self.lisa])
- # 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_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)
- 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.assertIs(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.guitar_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.assertIs(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.guitar_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.assertIs(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.guitar_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.assertIs(choice['selected'], True)
- self.assertEqual(
- choice['query_string'],
- '?date_registered__gte=%s&date_registered__lt=%s' % (
- str(self.one_week_ago),
- str(self.tomorrow),
- )
- )
- # Null/not null queries
- request = self.request_factory.get('/', {'date_registered__isnull': 'True'})
- changelist = self.get_changelist(request, Book, modeladmin)
- # Make sure the correct queryset is returned
- queryset = changelist.get_queryset(request)
- self.assertEqual(queryset.count(), 1)
- self.assertEqual(queryset[0], self.bio_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', 'No date')
- self.assertIs(choice['selected'], True)
- self.assertEqual(choice['query_string'], '?date_registered__isnull=True')
- request = self.request_factory.get('/', {'date_registered__isnull': 'False'})
- changelist = self.get_changelist(request, Book, modeladmin)
- # Make sure the correct queryset is returned
- queryset = changelist.get_queryset(request)
- self.assertEqual(queryset.count(), 3)
- self.assertEqual(list(queryset), [self.guitar_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', 'Has date')
- self.assertIs(choice['selected'], True)
- self.assertEqual(choice['query_string'], '?date_registered__isnull=False')
- @unittest.skipIf(
- sys.platform.startswith('win'),
- "Windows doesn't support setting a timezone that differs from the "
- "system timezone."
- )
- @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.assertIs(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.assertIs(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.guitar_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.assertIs(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.assertIs(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.assertIs(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.assertIs(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.assertIs(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.assertIs(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.assertIs(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.assertIs(choice['selected'], True)
- self.assertEqual(choice['query_string'], '?books_contributed__id__exact=%d' % self.django_book.pk)
- # With one book, the list filter should appear because there is also a
- # (None) option.
- Book.objects.exclude(pk=self.djangonaut_book.pk).delete()
- filterspec = changelist.get_filters(request)[0]
- self.assertEqual(len(filterspec), 2)
- # With no books remaining, no list filters should appear.
- Book.objects.all().delete()
- filterspec = changelist.get_filters(request)[0]
- self.assertEqual(len(filterspec), 0)
- 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_underscorelookup_foreignkey(self):
- Department.objects.create(code='TEST', description='Testing')
- self.djangonaut_book.employee = self.john
- self.djangonaut_book.save()
- self.bio_book.employee = self.jack
- self.bio_book.save()
- modeladmin = BookAdminRelatedOnlyFilter(Book, site)
- request = self.request_factory.get('/')
- changelist = self.get_changelist(request, Book, modeladmin)
- # Only actual departments should be present in employee__department's
- # list filter.
- filterspec = changelist.get_filters(request)[0][6]
- expected = [
- (self.dev.code, str(self.dev)),
- (self.design.code, str(self.design)),
- ]
- 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.assertIs(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.guitar_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.assertIs(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.assertIs(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 (#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.assertIs(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.assertIs(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.assertIs(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.guitar_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.assertIs(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.assertIs(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.assertIs(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('/', {})
- msg = "The list filter 'DecadeListFilterWithoutTitle' does not specify a 'title'."
- with self.assertRaisesMessage(ImproperlyConfigured, msg):
- 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('/', {})
- msg = "The list filter 'DecadeListFilterWithoutParameter' does not specify a 'parameter_name'."
- with self.assertRaisesMessage(ImproperlyConfigured, msg):
- 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 (#17828).
- """
- modeladmin = DecadeFilterBookAdminWithFailingQueryset(Book, site)
- request = self.request_factory.get('/', {})
- with 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.assertIs(choices[0]['selected'], True)
- self.assertEqual(choices[0]['query_string'], '?')
- self.assertEqual(choices[1]['display'], 'the 1990\'s')
- self.assertIs(choices[1]['selected'], False)
- self.assertEqual(choices[1]['query_string'], '?publication-decade=the+90s')
- self.assertEqual(choices[2]['display'], 'the 2000\'s')
- self.assertIs(choices[2]['selected'], False)
- self.assertEqual(choices[2]['query_string'], '?publication-decade=the+00s')
- def test_two_characters_long_field(self):
- """
- list_filter works with two-characters long field names (#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.assertIs(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' (#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.assertIs(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.assertIs(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 (#19318).
- """
- modeladmin = DepartmentFilterEmployeeAdmin(Employee, site)
- request = self.request_factory.get('/', {'department': self.john.department.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.assertIs(choices[1]['selected'], True)
- self.assertEqual(choices[1]['query_string'], '?department=%s' % self.john.department.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 (#19182).
- """
- modeladmin = DepartmentFilterUnderscoredEmployeeAdmin(Employee, site)
- request = self.request_factory.get('/', {'department__whatever': self.john.department.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.assertIs(choices[1]['selected'], True)
- self.assertEqual(choices[1]['query_string'], '?department__whatever=%s' % self.john.department.pk)
- def test_fk_with_to_field(self):
- """
- A filter on a FK respects the FK's to_field attribute (#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.assertIs(choices[0]['selected'], True)
- self.assertEqual(choices[0]['query_string'], '?')
- self.assertEqual(choices[1]['display'], 'Development')
- self.assertIs(choices[1]['selected'], False)
- self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV')
- self.assertEqual(choices[2]['display'], 'Design')
- self.assertIs(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.assertIs(choices[0]['selected'], False)
- self.assertEqual(choices[0]['query_string'], '?')
- self.assertEqual(choices[1]['display'], 'Development')
- self.assertIs(choices[1]['selected'], True)
- self.assertEqual(choices[1]['query_string'], '?department__code__exact=DEV')
- self.assertEqual(choices[2]['display'], 'Design')
- self.assertIs(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"))
- def test_list_filter_queryset_filtered_by_default(self):
- """
- A list filter that filters the queryset by default gives the correct
- full_result_count.
- """
- modeladmin = NotNinetiesListFilterAdmin(Book, site)
- request = self.request_factory.get('/', {})
- changelist = self.get_changelist(request, Book, modeladmin)
- changelist.get_results(request)
- self.assertEqual(changelist.full_result_count, 4)
|