Example #1
0
    def test_accepts_args_and_request(self):
        """
        Ensure that the subject is also handled if being
        passed a request object.
        """
        message = "Custom message that says '%s' and '%s'"
        token1 = 'ping'
        token2 = 'pong'

        logger = getLogger('djangocg.request')
        admin_email_handler = self.get_admin_email_handler(logger)
        # Backup then override original filters
        orig_filters = admin_email_handler.filters
        try:
            admin_email_handler.filters = []
            rf = RequestFactory()
            request = rf.get('/')
            logger.error(message, token1, token2,
                extra={
                    'status_code': 403,
                    'request': request,
                }
            )
            self.assertEqual(len(mail.outbox), 1)
            self.assertEqual(mail.outbox[0].to, ['*****@*****.**'])
            self.assertEqual(mail.outbox[0].subject,
                             "-SuperAwesomeSubject-ERROR (internal IP): Custom message that says 'ping' and 'pong'")
        finally:
            # Restore original filters
            admin_email_handler.filters = orig_filters
Example #2
0
    def test_request_factory(self):
        factory = RequestFactory()
        request = factory.get('/somewhere/')
        response = get_view(request)

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'This is a test')
Example #3
0
class ETagGZipMiddlewareTest(TestCase):
    """
    Tests if the ETag middleware behaves correctly with GZip middleware.
    """
    compressible_string = b'a' * 500

    def setUp(self):
        self.rf = RequestFactory()

    def test_compress_response(self):
        """
        Tests that ETag is changed after gzip compression is performed.
        """
        request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
        response = GZipMiddleware().process_response(request,
            CommonMiddleware().process_response(request,
                HttpResponse(self.compressible_string)))
        gzip_etag = response.get('ETag')

        request = self.rf.get('/', HTTP_ACCEPT_ENCODING='')
        response = GZipMiddleware().process_response(request,
            CommonMiddleware().process_response(request,
                HttpResponse(self.compressible_string)))
        nogzip_etag = response.get('ETag')

        self.assertNotEqual(gzip_etag, nogzip_etag)
Example #4
0
    def test_secure_session_cookie(self):
        request = RequestFactory().get('/')
        response = HttpResponse('Session test')
        middleware = SessionMiddleware()

        # Simulate a request the modifies the session
        middleware.process_request(request)
        request.session['hello'] = 'world'

        # Handle the response through the middleware
        response = middleware.process_response(request, response)
        self.assertTrue(
            response.cookies[settings.SESSION_COOKIE_NAME]['secure'])
Example #5
0
    def test_session_save_on_500(self):
        request = RequestFactory().get('/')
        response = HttpResponse('Horrible error')
        response.status_code = 500
        middleware = SessionMiddleware()

        # Simulate a request the modifies the session
        middleware.process_request(request)
        request.session['hello'] = 'world'

        # Handle the response through the middleware
        response = middleware.process_response(request, response)

        # Check that the value wasn't saved above.
        self.assertNotIn('hello', request.session.load())
Example #6
0
 def setUp(self):
     self.request_factory = RequestFactory()
     b1 = Band(name='Aerosmith', bio='', rank=3)
     b1.save()
     b2 = Band(name='Radiohead', bio='', rank=1)
     b2.save()
     b3 = Band(name='Van Halen', bio='', rank=2)
     b3.save()
Example #7
0
    def test_fix_IE_for_vary(self):
        """
        Regression for #16632.

        `fix_IE_for_vary` shouldn't crash when there's no Content-Type header.
        """

        # functions to generate responses
        def response_with_unsafe_content_type():
            r = HttpResponse(content_type="text/unsafe")
            r['Vary'] = 'Cookie'
            return r

        def no_content_response_with_unsafe_content_type():
            # 'Content-Type' always defaulted, so delete it
            r = response_with_unsafe_content_type()
            del r['Content-Type']
            return r

        # request with & without IE user agent
        rf = RequestFactory()
        request = rf.get('/')
        ie_request = rf.get('/', HTTP_USER_AGENT='MSIE')

        # not IE, unsafe_content_type
        response = response_with_unsafe_content_type()
        utils.fix_IE_for_vary(request, response)
        self.assertTrue('Vary' in response)

        # IE, unsafe_content_type
        response = response_with_unsafe_content_type()
        utils.fix_IE_for_vary(ie_request, response)
        self.assertFalse('Vary' in response)

        # not IE, no_content
        response = no_content_response_with_unsafe_content_type()
        utils.fix_IE_for_vary(request, response)
        self.assertTrue('Vary' in response)

        # IE, no_content
        response = no_content_response_with_unsafe_content_type()
        utils.fix_IE_for_vary(ie_request, response)
        self.assertFalse('Vary' in response)
Example #8
0
class TestAdminOrdering(TestCase):
    """
    Let's make sure that ModelAdmin.queryset uses the ordering we define in
    ModelAdmin rather that ordering defined in the model's inner Meta
    class.
    """

    def setUp(self):
        self.request_factory = RequestFactory()
        b1 = Band(name='Aerosmith', bio='', rank=3)
        b1.save()
        b2 = Band(name='Radiohead', bio='', rank=1)
        b2.save()
        b3 = Band(name='Van Halen', bio='', rank=2)
        b3.save()

    def test_default_ordering(self):
        """
        The default ordering should be by name, as specified in the inner Meta
        class.
        """
        ma = ModelAdmin(Band, None)
        names = [b.name for b in ma.queryset(request)]
        self.assertEqual(['Aerosmith', 'Radiohead', 'Van Halen'], names)

    def test_specified_ordering(self):
        """
        Let's use a custom ModelAdmin that changes the ordering, and make sure
        it actually changes.
        """
        class BandAdmin(ModelAdmin):
            ordering = ('rank',) # default ordering is ('name',)
        ma = BandAdmin(Band, None)
        names = [b.name for b in ma.queryset(request)]
        self.assertEqual(['Radiohead', 'Van Halen', 'Aerosmith'], names)

    def test_dynamic_ordering(self):
        """
        Let's use a custom ModelAdmin that changes the ordering dinamically.
        """
        super_user = User.objects.create(username='******', is_superuser=True)
        other_user = User.objects.create(username='******')
        request = self.request_factory.get('/')
        request.user = super_user
        ma = DynOrderingBandAdmin(Band, None)
        names = [b.name for b in ma.queryset(request)]
        self.assertEqual(['Radiohead', 'Van Halen', 'Aerosmith'], names)
        request.user = other_user
        names = [b.name for b in ma.queryset(request)]
        self.assertEqual(['Aerosmith', 'Radiohead', 'Van Halen'], names)
Example #9
0
    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)

        self.request_factory = RequestFactory()

        # Users
        self.alfred = User.objects.create_user('alfred', '*****@*****.**')
        self.bob = User.objects.create_user('bob', '*****@*****.**')
        self.lisa = User.objects.create_user('lisa', '*****@*****.**')

        # 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()
Example #10
0
 def setUp(self):
     self.factory = RequestFactory()
Example #11
0
class TemplateResponseTest(TestCase):

    def setUp(self):
        self.factory = RequestFactory()

    def _response(self, template='foo', *args, **kwargs):
        return TemplateResponse(self.factory.get('/'), Template(template),
                                *args, **kwargs)

    def test_render(self):
        response = self._response('{{ foo }}{{ processors }}').render()
        self.assertEqual(response.content, b'yes')

    def test_render_with_requestcontext(self):
        response = self._response('{{ foo }}{{ processors }}',
                                  {'foo': 'bar'}).render()
        self.assertEqual(response.content, b'baryes')

    def test_render_with_context(self):
        response = self._response('{{ foo }}{{ processors }}',
                                  Context({'foo': 'bar'})).render()
        self.assertEqual(response.content, b'bar')

    def test_kwargs(self):
        response = self._response(content_type = 'application/json',
                                  status=504)
        self.assertEqual(response['content-type'], 'application/json')
        self.assertEqual(response.status_code, 504)

    def test_args(self):
        response = TemplateResponse(self.factory.get('/'), '', {},
                                    'application/json', 504)
        self.assertEqual(response['content-type'], 'application/json')
        self.assertEqual(response.status_code, 504)

    def test_custom_app(self):
        response = self._response('{{ foo }}', current_app="foobar")

        rc = response.resolve_context(response.context_data)

        self.assertEqual(rc.current_app, 'foobar')

    def test_pickling(self):
        # Create a template response. The context is
        # known to be unpickleable (e.g., a function).
        response = TemplateResponse(self.factory.get('/'),
            'first/test.html', {
                'value': 123,
                'fn': datetime.now,
            })
        self.assertRaises(ContentNotRenderedError,
                          pickle.dumps, response)

        # But if we render the response, we can pickle it.
        response.render()
        pickled_response = pickle.dumps(response)
        unpickled_response = pickle.loads(pickled_response)

        self.assertEqual(unpickled_response.content, response.content)
        self.assertEqual(unpickled_response['content-type'], response['content-type'])
        self.assertEqual(unpickled_response.status_code, response.status_code)

        # ...and the unpickled reponse doesn't have the
        # template-related attributes, so it can't be re-rendered
        template_attrs = ('template_name', 'context_data',
            '_post_render_callbacks', '_request', '_current_app')
        for attr in template_attrs:
            self.assertFalse(hasattr(unpickled_response, attr))

        # ...and requesting any of those attributes raises an exception
        for attr in template_attrs:
            with self.assertRaises(AttributeError):
                getattr(unpickled_response, attr)

    def test_repickling(self):
        response = SimpleTemplateResponse('first/test.html', {
                'value': 123,
                'fn': datetime.now,
            })
        self.assertRaises(ContentNotRenderedError,
                          pickle.dumps, response)

        response.render()
        pickled_response = pickle.dumps(response)
        unpickled_response = pickle.loads(pickled_response)
        repickled_response = pickle.dumps(unpickled_response)
Example #12
0
 def setUp(self):
     self.rf = RequestFactory()
Example #13
0
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)

        self.request_factory = RequestFactory()

        # Users
        self.alfred = User.objects.create_user('alfred', '*****@*****.**')
        self.bob = User.objects.create_user('bob', '*****@*****.**')
        self.lisa = User.objects.create_user('lisa', '*****@*****.**')

        # 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()

    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_query_set(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.tomorrow})
        changelist = self.get_changelist(request, Book, modeladmin)

        # Make sure the correct queryset is returned
        queryset = changelist.get_query_set(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.tomorrow))

        request = self.request_factory.get('/', {'date_registered__gte': self.today.replace(month=1, day=1),
                                                 'date_registered__lt': self.tomorrow})
        changelist = self.get_changelist(request, Book, modeladmin)

        # Make sure the correct queryset is returned
        queryset = changelist.get_query_set(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.tomorrow))

        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_query_set(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_query_set(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_relatedfieldlistfilter_foreignkey(self):
        modeladmin = BookAdmin(Book, site)

        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_query_set(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('/', {'contributors__isnull': 'True'})
        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.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_query_set(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_query_set(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_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_query_set(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_query_set(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_query_set(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_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_query_set(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_query_set(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_query_set(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_query_set(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_query_set(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'], '?publication-decade=the+00s&author__id__exact=%s' % 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'], '?publication-decade=the+00s&author__id__exact=%s' % self.alfred.pk)

    def test_listfilter_without_title(self):
        """
        Any filter must define a title.
        """
        modeladmin = DecadeFilterBookAdminWithoutTitle(Book, site)
        request = self.request_factory.get('/', {})
        self.assertRaisesRegexp(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('/', {})
        self.assertRaisesRegexp(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_query_set(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_query_set(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_query_set(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_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)

        dev = Department.objects.create(code='DEV', description='Development')
        design = Department.objects.create(code='DSN', description='Design')
        john = Employee.objects.create(name='John Blue', department=dev)
        jack = Employee.objects.create(name='Jack Red', department=design)

        request = self.request_factory.get('/', {})
        changelist = self.get_changelist(request, Employee, modeladmin)

        # Make sure the correct queryset is returned
        queryset = changelist.get_query_set(request)
        self.assertEqual(list(queryset), [jack, 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_query_set(request)
        self.assertEqual(list(queryset), [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')