Opened 12 years ago

Last modified 4 months ago

#8851 new New feature

Add a default option to list_filter in the admin interface

Reported by: Riskable <riskable@…> Owned by: Harro
Component: contrib.admin Version: master
Severity: Normal Keywords: admin, list_filter, default
Cc: Carl Meyer, Trevor Caira, rlaager@…, andy@…, wizard@…, kc9ddi, remco@…, net147, trbs@…, a@…, markus.magnuson@…, andreas@…, simon@…, Narbonne Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


There should be a way to set a default list_filter in the admin interface. For example, if I had a model like this:

        ('N', 'No'),
        ('Y', 'Yes'),

class Host(models.Model):
        hostname = models.CharField(max_length=36, unique=True)
        decommissioned = models.CharField(max_length=1, choices=DECOM_CHOICES, default='N')
        ip_address = models.IPAddressField()
        def __unicode__(self):
                return self.hostname

class HostAdmin(admin.ModelAdmin):
        fieldsets = [
                ('Host Info', {'fields': ['hostname','decommissioned','ip_address']}),
        list_display = ('hostname', 'ip_address', 'decommissioned')
        list_filter = ('decommissioned'), HostAdmin)

I might want to make it so that by default only hosts that are not marked as decommissioned are displayed by default in the index. It would be nice if I could just pass an option to list_filter to set the a filter that is enabled by default. Perhaps something like this:

list_filter = ('decommissioned__exact=N') # Matches the URL if you click on "No" in the filter list

...or perhaps...

list_filter = ('decommissioned__default') # Have it choose the same default as specified in the model.

If there's already a simple way to do this I haven't found it and would appreciate an example.


Change History (45)

comment:1 Changed 12 years ago by Malcolm Tredinnick

milestone: 1.0

comment:2 Changed 11 years ago by Jacob

Triage Stage: UnreviewedAccepted

Not a big fan of the syntax, but a useful feature.

comment:3 Changed 11 years ago by Riskable

The syntax doesn't matter... The feature is what matters =). If you're going to implement this you can do it however you like (and I thank you!).

comment:4 Changed 11 years ago by nitipit

Resolution: fixed
Status: newclosed

That's a great idea to add this feature.
for now, I make a bit hack of code to set django.contrib.admin.filterspecs.RelatedFilterSpec.lookup_choices to get objects from "ModelAdmin.formfield_for_foreignkey()"

class RelatedFilterSpec(FilterSpec):
    def __init__(self, f, request, params, model, model_admin):
        super(RelatedFilterSpec, self).__init__(f, request, params, model, model_admin)
        if isinstance(f, models.ManyToManyField):
            self.lookup_title =
            self.lookup_title = f.verbose_name
        rel_name = f.rel.get_related_field().name
        self.lookup_kwarg = '%s__%s__exact' % (, rel_name)
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
#       self.lookup_choices = f.rel.get_choices(include_blank=False); >> f.rel.get_choices will return lists of object tuple
        self.lookup_choices = [] ;
        for object in model_admin.formfield_for_foreignkey(f,request).queryset :
            list = (,object.__str__()) ;

Then just use formfield_for_foreignkey() which is described in django document to filter the objects we want.

comment:5 Changed 11 years ago by Karen Tracey

Resolution: fixed
Status: closedreopened

A workaround in a comment doesn't make the ticket fixed. The ticket is fixed when a change is checked into the Django source tree.

comment:6 Changed 11 years ago by Carl Meyer

Cc: Carl Meyer added

comment:7 Changed 11 years ago by anonymous

Cc: Trevor Caira added

comment:8 Changed 11 years ago by anonymous

Cc: rlaager@… added

comment:9 Changed 10 years ago by anonymous

Cc: andy@… added

comment:10 Changed 10 years ago by wizard@…

Cc: wizard@… added

Is this something that just needs committing?

comment:11 Changed 10 years ago by kc9ddi

Cc: kc9ddi added

comment:12 Changed 9 years ago by Remco Wendt

Cc: remco@… added

comment:13 Changed 9 years ago by Luke Plant

Severity: Normal
Type: New feature

comment:14 Changed 9 years ago by Sindre Sorhus

Easy pickings: unset

This would be very useful. Hoping for this in 1.4.

There is a ugly workaround on StackOverflow, but this does not show the default filter in the filterbar.

comment:15 Changed 8 years ago by Aymeric Augustin

UI/UX: unset

Change UI/UX from NULL to False.

comment:16 Changed 8 years ago by net147

Cc: net147 added

comment:17 Changed 8 years ago by Simon Charette

I think you can achieve this in 1.4+ by subclassing django.contrib.admin.ChoicesFieldListFilter.

comment:18 Changed 7 years ago by Aymeric Augustin

Status: reopenednew

comment:19 Changed 7 years ago by Tomek Paczkowski

Resolution: fixed
Status: newclosed

This feature has been already introduced in Django, unfortunately, I don't know when. Basically, subclass SimpleListFilter and override two methods lookups and queryset. You can read about it in ModelAdmin.list_filter docs

I'm closing this ticket as fixed, as described feature is in my opinion what you were looking for. If you feel it's not what you need, please reopen with detailed description what should be changed.

comment:20 Changed 7 years ago by schrinaw@…

Resolution: fixed
Status: closednew

I would argue that this is not fixed. I believe I have the same issue as the OP - I want to choose which of the available filter options should be selected by default. I don't want to customize the filtering behavior by subclassing SimpleListFilter. The desired behavior is that when I go to admin changelist url, it is *as if I had already chosen one of the filtering options*. I don't want to change the behavior of the filtering options. I just want one (the defined default) to be pre-selected.

comment:21 Changed 6 years ago by trbs

Cc: trbs@… added

comment:22 Changed 5 years ago by Chaim Kirby

Owner: changed from nobody to Chaim Kirby
Status: newassigned

comment:23 Changed 5 years ago by Chaim Kirby

Owner: Chaim Kirby deleted
Status: assignednew

comment:24 Changed 5 years ago by Harro

Owner: set to Harro
Status: newassigned

Working on this during duth sprints

comment:25 Changed 5 years ago by Harro

Has patch: set

comment:26 Changed 5 years ago by André Avorio

Cc: a@… added

comment:27 Changed 5 years ago by André Avorio

I'm willing to test this out once a patch has been committed.

Implemented simply, as originally described, this would be a very useful feature indeed.

comment:28 Changed 5 years ago by Tim Graham

While testing after commit is certainly helpful, if you can give feedback on and/or test the pull request before it's committed, that's even more helpful.

comment:29 Changed 5 years ago by Harro

Patch needs improvement: set

Just did some testing as well, and the filters selected value does not work correctly with the defaults. Working on a fix.

comment:30 Changed 5 years ago by Harro

Patch needs improvement: unset

Totally forgot to update the ticket, it's all done and ready now.

Should note that the fix was to actually NOT use request.GET at all in the filters, but the passed in params dictionary which contains the by the ChangeList preprocessed data.

I think this was always meant to be like this, but was never caught because filters are always only tested through the changelist.

Not sure if we want to slow down the build process even more with more detailed unittests.

Last edited 5 years ago by Harro (previous) (diff)

comment:31 Changed 5 years ago by Markus Amalthea Magnuson

Cc: markus.magnuson@… added

comment:32 Changed 5 years ago by Eric

+1 and bump, will this be included in 1.10 ?

comment:33 Changed 5 years ago by Tim Graham

You can help by reviewing the patch using the PatchReviewChecklist and marking the ticket "ready for checkin" when all looks good.

comment:34 Changed 4 years ago by Tim Graham

Patch needs improvement: set
Summary: Please add a default option to list_filter in the admin interfaceAdd a default option to list_filter in the admin interface

Left some comments on the pull request. Not sure if the current approach is ideal with regard to reusing the choice logic from list filter classes.

comment:35 Changed 4 years ago by Asif Saifuddin Auvi

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:36 Changed 4 years ago by Tim Graham

Triage Stage: Ready for checkinAccepted

Asif set the "Ready for Checkin" flag without reviewing the patch.

comment:37 Changed 4 years ago by Andreas Pelme

Cc: andreas@… added

comment:38 Changed 3 years ago by Simon Lightfoot

Cc: simon@… added

comment:39 Changed 3 years ago by Harro

Has patch: unset

Also see:

I think the solution to change the url linking to the list view is a much better one that what I've implemented, I'm closing my pull request and will do some research into the other solution.

comment:40 Changed 3 years ago by Harro

Ok, I found a much nicer solution for this whole thing, no need to do changes to urls at all.

Say you have an is_archived flag on a model and you want to default filter the list to hide archived items:

class ArchivedListFilter(admin.SimpleListFilter):

    title = _('Archived')
    parameter_name = 'show_archived'

    def lookups(self, request, model_admin):
        return (
            ('all', _('Show all')),
            ('archived', _('Show archived only')),

    def queryset(self, request, queryset):
        show_archived = request.GET.get('show_archived')
        if (show_archived == 'all'):
            return queryset.all()
        elif (show_archived == 'archived'):
            return queryset.filter(is_archived=True)
        return queryset.filter(is_archived=False)

    def choices(self, cl):
        yield {
            'selected': self.value() is None,
            'query_string': cl.get_query_string({}, [self.parameter_name]),
            'display': _('Hide archived'),  # Changed All to Hide archived.
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == force_text(lookup),
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,

Simply add this filter and by default it will filter and on filter values it will either show all items or only the archived items.

Now it would be nice to be able to change the label in for "All" in the choices method without the need to override whole method, so maybe the only fix would be to move the label to a propery on SimpleListFilter so you can quickly override it.

comment:41 Changed 3 years ago by Harro

Resolution: wontfix
Status: assignedclosed

As said, there is a way to do this with a documented feature, so nothing to fix.

comment:42 Changed 2 years ago by Jonas Haag

That's a lot of code to copy and paste, and it's probably going to break when changes to the admin are being made.

I'd say reopen this.

comment:43 Changed 14 months ago by Aymeric Augustin

Resolution: wontfix
Status: closednew

Sorry for the close / reopen ping-pong... I think this is a worthwhile feature request. For example, when using soft-delete, by default, you don't want soft-deleted objects to show up in the admin. The API proposed in is interesting.

comment:44 Changed 14 months ago by Aymeric Augustin

Furthermore, while SimpleListFilter is a public API, FieldListFilter isn't. The docs note:

The FieldListFilter API is considered internal and might be changed.

There's no way to make a good list filter for handling soft-deletes with SimpleListFilter because it defaults to showing everything, which is the main behavior that needs changing.

Here's how I did it with FieldListFilter:

class DeactivableListFilter(admin.ListFilter):

    title = _("active")
    parameter_name = 'active'

    def __init__(self, request, params, model, model_admin):
        super().__init__(request, params, model, model_admin)
        if self.parameter_name in params:
            value = params.pop(self.parameter_name)
            self.used_parameters[self.parameter_name] = value

    def show_all(self):
        return self.used_parameters.get(self.parameter_name) == '__all__'

    def has_output(self):
        return True

    def choices(self, changelist):
        return [
                'selected': self.show_all(),
                'query_string': changelist.get_query_string({self.parameter_name: '__all__'}),
                'display': "Tout",
                'selected': not self.show_all(),
                'query_string': changelist.get_query_string(remove=[self.parameter_name]),
                'display': "Actif",

    def queryset(self, request, queryset):
        if not self.show_all():
            return queryset.filter(active=True)

    def expected_parameters(self):
        return [self.parameter_name]

comment:45 Changed 4 months ago by Narbonne

Cc: Narbonne added
Note: See TracTickets for help on using tickets.
Back to Top