Skip to main content

Command Palette

Search for a command to run...

Selection widget for which columns to display in Django admin

Updated

When working in the Django admin with a lot of users, you might sometimes want to offer the functionality to users to choose which columns they want to see in the changelist page. For some users some columns might be more relevant than to others.

One way to achieve this is by putting a button in the top right corner that will lead you to a intermediary page like this:

Using the FilteredSelectMultiple widget

For this you need a special django form, that will show the so called FilteredSelectMultiple widget that is shown above. This widget takes quite some effort to get working properly, as normally this only works out-of-the-box by using filter_horizontal = [...] or filter_vertical = [...] in a ModelAdmin.

The trick here is to add the class Media which will ensure that the correct javascript is loaded to make this widget work. As you see in the Gist below in filter_columns.html , you also need to include a stylesheet for the form.css or it will not look as nice.

from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple


class FilterColumnsForm(forms.Form):
    """Form with only one field to filter the columns.
    Django widget for horizontal selection, works only in the admin and only with media included.
    """
    filter_columns = forms.MultipleChoiceField(
        label=False,
        widget=FilteredSelectMultiple('columns', is_stacked=False)
    )

    class Media:
        js = ('/admin/jsi18n',)

This setup allows you to use FilteredSelectMultiple basically everywhere outside the regular admin configuration for if you want to customize the use or options.

ModelAdmin mixin to create an intermediary page

In get_urls() you want to append a new path that for the intermediary page for when you click on the "Columns" button. Based on the list_display parameter you can create some

def filter_columns(self, request):
    """View for intermediary page when clicking on the "Columns" button.
    Will render an admin view with standard context, showing the `FilterColumnsForm`.
    """
    cl = self.get_changelist_instance(request)

    # Determine which to display in the form and which are already selected
    display = []
    selected = []
    all_fields = type(self).list_display
    for field_name in all_fields:
        text = label_for_field(field_name, cl.model, model_admin=cl.model_admin, return_attr=False)
        display.append((field_name, text))
        if field_name in cl.list_display:
            selected.append(field_name)

    # Initialize form with selected and set the available choices
    form = FilterColumnsForm(initial={'filter_columns': selected})
    form['filter_columns'].field.choices = display

    # Add context for showing the menu and bars
    context = self.admin_site.each_context(request)
    context['title'] = _('Choose which columns to show')
    context['form'] = form
    context['opts'] = self.model._meta  # pylint: disable=protected-access
    request.current_app = self.admin_site.name
    return TemplateResponse(request, 'admin/filter_columns.html', context)

The whole code can be found here on Github:

Use this structure as it matters where the templates are placed, replace app by your own name:

app/admin.py
app/forms.py
app/templates/admin/filter_columns.html
app/templates/admin/change_list.html

Deep into Django

Part 1 of 9

Django offers a great deal of hidden functionality - secret gems - if you know where to look. In this serie I am showing tips and tricks to get the most out of Django.

Up next

Managing superusers when using the Django admin as a backend for employees

Expiring a superuser in Django after a due date