[Fixed]-Django Admin List Filter Remove All Option

15👍

I eventually solved it by browsing the source code which can be found [here in this link][1] and overriding the choices function. I only changed the "All" selection to show "Yes" in this code that was added to my filter class :

from django.utils.encoding import force_text

def choices(self, changelist):
    """Copied from source code to remove the "All" Option"""
    yield {
        'selected': self.value() is None,
        'query_string': changelist.get_query_string({}, [self.parameter_name]),
        'display': 'Yes',
    }
    for lookup, title in self.lookup_choices:
        yield {
            'selected': self.value() == force_text(lookup),
            'query_string': changelist.get_query_string({self.parameter_name: lookup}, []),
            'display': title,
        }

As Linh mentioned: For anyone only want to remove the all option just delete 'display': 'Yes', from the first yield{} in choices function
[1]: https://github.com/django/django/blob/main/django/contrib/admin/filters.py#L44

1👍

Perhaps even cleaner, if you’re looking to remove the All option entirely, is simply to pop off (consume) the first item from the generator. The yield in the source code you found in the Django source is a python keyword that means ‘each time you ask for a new value, return the next one, until we run out’

It makes it possible to have a big list of things but only load one into memory at a time and some other neat side benefits. You trigger the consume behavior when you add the generator into a loop, but you can do it one at a time with the next function.

So this would be a really nice way to pop off the ‘All’ while still working with any changes Django makes in the future:

class DealExpiredListFilter(admin.SimpleListFilter):
    def choices(self, changelist):
        choices = super().choices(changelist)
        next(choices)
        return choices

0👍

You can copy, paste and override choices() then uncomment "display": _("All"), as shown below to remove All option from Django Admin filter:

class DealExpiredListFilter(admin.SimpleListFilter):
    def choices(self, changelist): # Here
        yield {
            "selected": self.value() is None,
            "query_string": changelist.get_query_string(remove=[self.parameter_name]),
            # "display": _("All"), # Here
        }
        for lookup, title in self.lookup_choices:
            yield {
                "selected": self.value() == str(lookup),
                "query_string": changelist.get_query_string(
                    {self.parameter_name: lookup}
                ),
                "display": title,
            }

0👍

All or Nothing?

This is a solution based on the above answers, but using Django 4.1. Django 4.1 implements a Clear all filters button (#27888 closed Cleanup/optimization (fixed) Add a button to clear all admin filters) which appears after the admin user has made a filter selection, and when pressed essentially provides the All option by the back door as it were. In addition there is the question of what the page should display before the admin user has made a filter selection at all – again this is the same as the All option. In my implementation it makes no sense to be able to view all the records (races) for all events (the fk filter) together in a huge list, I only want to be able to view all the races for a single event at a time. When you provide a custom list filter class you are required to implement lookups and queryset functions as described by the op. Here are mine:

class NoAllListFilter(admin.SimpleListFilter):
    title = 'event'
    parameter_name = 'event__name'

    def lookups(self, request, model_admin):
        return (
            tuple((event.name, event.name) for event in Event.objects.all())
        )

    def queryset(self, request, queryset):
        return queryset.filter(event__name = self.value())

My queryset function is different to the documented example in ModelAdmin List Filters in that it does not check self.value() first, so when no event selection has been made, or Clear all filters is pressed, it provides a queryset which doesn’t match any records at all, which is actually what I want.

By contrast, this modified queryset function, which is more like the documented example, but which does not suit my purpose, produces a queryset which displays all records (all races in all events) when no event selection has been made, or when Clear all filters is pressed (counter-example only – not used):

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(event__name = self.value())

Returning to my first, implemented, queryset function (not the counter example), to align with the 0 races displayed when no event selection has been made, or when Clear all filters is pressed, instead of commenting out the initially generated choices yield, I changed the display from All to None. Logically Clear all filters really, and in most cases, means don’t filter anything and display all records, but in this instance I think the None option replacing the All makes it clear that no records are expected to be displayed. Here is my modified Django 4.1 choices function in the NoAllListFilter class:

    def choices(self, changelist):
        # Modified from Django 4.1 source
        yield {
            "selected": self.value() is None,
            "query_string": changelist.get_query_string(remove=[self.parameter_name]),
            # "display": _("All"),
            "display": _("None"),
        }
        for lookup, title in self.lookup_choices:
            yield {
                "selected": self.value() == str(lookup),
                "query_string": changelist.get_query_string(
                    {self.parameter_name: lookup}
                ),
                "display": title,
            }

Note that the comment that the method force_text has been deprecated from Django 4.0 onwards is doubtless correct but no longer applicable because the Django 4.1 choices function just uses str(lookup) rather than force_text(lookup)

Leave a comment