django-multiselectfield icon indicating copy to clipboard operation
django-multiselectfield copied to clipboard

list_filter behaviour yields empty lists

Open ZippoLag opened this issue 5 years ago • 5 comments

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.

ZippoLag avatar Jun 10 '20 22:06 ZippoLag

Hi! @ZippoLag, any luck with the filter?

concongo avatar Aug 31 '20 17:08 concongo

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

FedeG avatar Jan 13 '21 21:01 FedeG

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)

benoitperrin avatar Mar 10 '21 17:03 benoitperrin

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

hetdev avatar Apr 14 '21 01:04 hetdev

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")]

leifdenby avatar May 27 '23 12:05 leifdenby