Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list_filter behaviour yields empty lists #116

Open
ZippoLag opened this issue Jun 10, 2020 · 5 comments
Open

list_filter behaviour yields empty lists #116

ZippoLag opened this issue Jun 10, 2020 · 5 comments

Comments

@ZippoLag
Copy link

If we include a MultiSelectField in a model's Admin list_filter then when we click on any of the "filter" links in the list page we get redirect to a URL that uses ?field_name__exact=choice and hence returns no valid results.

However, tampering with the URL and changing it to ?field_name__icontains=choice works as expected.

I've poked around and I believe that we could create our own filter derived from admin.ChoicesFieldListFilter (defined in filters.py), but I'm not sure even where to begin with.

@concongo
Copy link

Hi! @ZippoLag, any luck with the filter?

@FedeG
Copy link

FedeG commented Jan 13, 2021

Workaround:

class FieldNameFilter(admin.ChoicesFieldListFilter):
    title = 'field_name'
    parameter_name = 'field_name'

    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(field, request, params, model, model_admin, field_path)
        self.lookup_kwarg = '%s__icontains' % field_path

@benoitperrin
Copy link

benoitperrin commented Mar 10, 2021

Thanks @FedeG for providing this workaround. For the "selected" option to work, I had to override both lookup_kwarg and lookup_val (see below code).

class FieldNameFilter(admin.ChoicesFieldListFilter):
    title = 'field_name'
    parameter_name = 'field_name'

    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(field, request, params, model, model_admin, field_path)
        self.lookup_kwarg = '%s__icontains' % field_path
        self.lookup_val = params.get(self.lookup_kwarg)

@hetdev
Copy link

hetdev commented Apr 14, 2021

Workaround2

Based on the docs multiselect storage a charfield with the options selected separated by commas
you can use the endswith to get when the user selects only one option, and the icontains to get when the user choose more than one option.

class MultiSelectFilter(admin.SimpleListFilter):
    # Human-readable title which will be displayed in the
    # right admin sidebar just above the filter options.

    title = param_title
    parameter_name = param_name

    def lookups(self, request, model_admin):
        return ( ('option 1', 'Option 1'),
                     ('option 2', 'Option 2')) # tuple ('lookup_value', 'tittle_value in list filter')

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(Q(**{
                f'{self.parameter_name}__iendswith': self.value()
            }) | Q(**{
                f'{self.parameter_name}__icontains': f'{self.value()},'
            }))
        return queryset

@leifdenby
Copy link

Thanks for the above! Using that I've hacked something together which can be used with list_filter on a admin.ModelAdmin:

def _multiple_choice_filter(field_name):
    class FieldNameFilter(admin.ChoicesFieldListFilter):
        title = field_name
        parameter_name = field_name

        def __init__(self, field, request, params, model, model_admin, field_path):
            super().__init__(field, request, params, model, model_admin, field_path)
            self.lookup_kwarg = "%s__icontains" % field_path
            self.lookup_val = params.get(self.lookup_kwarg)

    return (field_name, FieldNameFilter)

class ObjectAdmin(admin.ModelAdmin):
   list_filter = [_multiple_choice_filter("field_name")]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants