Commit c4db7f07 authored by Anentropic's avatar Anentropic Committed by Tim Graham
Browse files

Fixed #19182 -- Fixed ModelAdmin.lookup_allowed to work with ('fieldname',...

Fixed #19182 -- Fixed ModelAdmin.lookup_allowed to work with ('fieldname', SimpleListFilter) syntax.

Thanks gauss for the report.
parent 62dfd79f
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -319,6 +319,8 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
        return qs

    def lookup_allowed(self, lookup, value):
        from django.contrib.admin.filters import SimpleListFilter

        model = self.model
        # Check FKey lookups that are allowed, so that popups produced by
        # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
@@ -365,7 +367,15 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
        if len(parts) == 1:
            return True
        clean_lookup = LOOKUP_SEP.join(parts)
        return clean_lookup in self.list_filter or clean_lookup == self.date_hierarchy
        valid_lookups = [self.date_hierarchy]
        for filter_item in self.list_filter:
            if isinstance(filter_item, type) and issubclass(filter_item, SimpleListFilter):
                valid_lookups.append(filter_item.parameter_name)
            elif isinstance(filter_item, (list, tuple)):
                valid_lookups.append(filter_item[0])
            else:
                valid_lookups.append(filter_item)
        return clean_lookup in valid_lookups

    def has_add_permission(self, request):
        """
+71 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
import datetime

from django.contrib.admin import (site, ModelAdmin, SimpleListFilter,
    BooleanFieldListFilter)
    BooleanFieldListFilter, AllValuesFieldListFilter)
from django.contrib.admin.views.main import ChangeList
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
@@ -38,26 +38,32 @@ class DecadeListFilter(SimpleListFilter):
        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):
@@ -69,10 +75,12 @@ class DecadeListFilterWithQuerysetBasedLookups(DecadeListFilterWithTitleAndParam
        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"
@@ -93,41 +101,61 @@ class DepartmentListFilterLookupWithNonStringValue(SimpleListFilter):
        if self.value():
            return queryset.filter(department__id=self.value())


class DepartmentListFilterLookupWithUnderscoredParameter(DepartmentListFilterLookupWithNonStringValue):
    parameter_name = 'department__whatever'


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 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']
@@ -137,6 +165,10 @@ class DepartmentFilterEmployeeAdmin(EmployeeAdmin):
    list_filter = [DepartmentListFilterLookupWithNonStringValue, ]


class DepartmentFilterUnderscoredEmployeeAdmin(EmployeeAdmin):
    list_filter = [DepartmentListFilterLookupWithUnderscoredParameter, ]


class ListFiltersTests(TestCase):

    def setUp(self):
@@ -453,6 +485,23 @@ class ListFiltersTests(TestCase):
        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)

@@ -692,6 +741,27 @@ class ListFiltersTests(TestCase):
        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.