[Django]-How to change the Django admin filter to use a dropdown instead of list?

64👍

Thanks @beholderrk, @gediminas and @jk-laiho! I packaged this into a reusable app.

Install:

pip install django-admin-list-filter-dropdown

Enable in settings.py:

INSTALLED_APPS = (
    ...
    'django_admin_listfilter_dropdown',
    ...
)

Use in admin.py:

from django_admin_listfilter_dropdown.filters import (
    DropdownFilter, ChoiceDropdownFilter, RelatedDropdownFilter
)

class EntityAdmin(admin.ModelAdmin):
    ...
    list_filter = (
        # for ordinary fields
        ('a_charfield', DropdownFilter),
        # for choice fields
        ('a_choicefield', ChoiceDropdownFilter),
        # for related fields
        ('a_foreignkey_field', RelatedDropdownFilter),
    )

Here’s what it looks like:

Screenshot of dropdown list filter

👤mrts

41👍

I cannot comment answers so I’ll add to beholderrk’s answer here.

  1. create a new template called dropdown_filter.html or similar
  2. copy the code of filter.html from feincms to dropdown_filter.html
  3. create a new filter class in filters.py:

    from django.contrib.admin.filters import AllValuesFieldListFilter
    
    class DropdownFilter(AllValuesFieldListFilter):
        template = 'admin/dropdown_filter.html'
    
  4. now you can use this filter in your admin class:

    class SomeAdmin(admin.ModelAdmin):
        # ...
        list_filter = (('country', DropdownFilter),)
    

Works great!

33👍

Use filter.html from feincms

{% load i18n %}
<script type="text/javascript">var go_from_select = function(opt) { window.location = window.location.pathname + opt };</script>
<h3>{{ title }}</h3>
<ul class="admin-filter-{{ title|cut:' ' }}">
{% if choices|slice:"4:" %}
    <li>
    <select style="width: 95%;"
        onchange="go_from_select(this.options[this.selectedIndex].value)">
    {% for choice in choices %}
        <option{% if choice.selected %} selected="selected"{% endif %}
         value="{{ choice.query_string|iriencode }}">{{ choice.display }}</option>
    {% endfor %}
    </select>
    </li>
{% else %}

    {% for choice in choices %}
            <li{% if choice.selected %} class="selected"{% endif %}>
            <a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li>
    {% endfor %}

{% endif %}
</ul>

4👍

An easy option would be to use django-grappelli, which replaces all the filters with drop downs.

3👍

You can copy the admin templates from the django installation into you templates/admin folder in your project.

Then you will need to do any of 2 things in the forms or templates you want to show your outputs in:

  1. If you are working with a form, in that you would like the list choices to be posted back to a database, you would in your model.py, on the field you have your choices, put in some this like this:

    choice = forms.IntegerField(widget=forms.Select(choices=CHOICES))
    
  2. If it is just to display on a page, then you will output on a template tag something like this:

    <select>
      {% for choices in object.details.all %}
        <option> {{ object.choice }} </option>
      {% endfor %}
    </select>
    

2👍

http://djangosuit.com/ also offers dropdowns for list filters.

2👍

I am not a fan of all solutions provided up to now.

Why? If, for a field that you want to filter by, you have more than 10 values, a listview box isn’t that handy, too. I advice to use the standard search field capability of django admin which will show you a search field:

class BooksAdmin(admin.ModelAdmin):
    list_display = ('ISBN', 'title')         
    search_fields = ('ISBN',)
    # instead of: list_filter = ('ISBN',)
    ordering = ('title',)  

0👍

The best solution is to create a new template in admin/filter.html and implement the HTML code suggested by @beholderrk. Just implemented it for a client and it works great.

Problem with DropdownFilter and RelatedDropdownFilter is that it loses the proper display. Instead of the translated strings for Charfield(choices=xxx), it will show True, False and so on.

👤Özer

0👍

Could you please give a complete example. it shows like before.
here is my code

from django.contrib import admin
from pages.models import Post, Device, DeviceType, DeviceModel, Ipaddress, DeviceGroup, Location,Department,Comment
from django_admin_listfilter_dropdown.filters import DropdownFilter, RelatedDropdownFilter


class CommentInline(admin.TabularInline):
    model = Comment

class IpaddressAdmin(admin.ModelAdmin):
        prepopulated_fields = {'slug': ('ipaddress',)}
#        model=Ipaddress

        search_fields = ['ipaddress', ]
#     
        list_display = ('ipaddress', 'machinename', 'user', 'department','location',)
        list_filter = (
        ('user', DropdownFilter),
        ('department', RelatedDropdownFilter),
        ('location', RelatedDropdownFilter),

    )

Here is the screenshotenter image description here

👤Rana

0👍

I was struggling with the same problem some few weeks back. So this answer might be useful to some developers from the future.

I managed to solve the problem by writing a custom template.html

I have bundled the code in an amazing package now that does the same for you, here’s the link.


Here’s how you can implement a Searchable Dropdown in place of the default List:

1. Installation:

pip install django-admin-searchable-dropdown

This command will install the latest version of the package in your project.
Now, include the package in your project by adding admin_searchable_dropdown to your INSTALLED_APPS inside settings.py file.

2. Usage:
Let’s say you have following models:

from django.db import models

class CarCompany(models.Model):
    name = models.CharField(max_length=128)

class CarModel(models.Model):
    name = models.CharField(max_length=64)
    company = models.ForeignKey(CarCompany, on_delete=models.CASCADE)

And you would like to filter results in CarModelAdmin on the basis of company. You need to define search_fields in CarCompany and then define filter like this:

from django.contrib import admin
from admin_searchable_dropdown.filters import AutocompleteFilter


class CarCompanyFilter(AutocompleteFilter):
    title = 'Company' # display title
    field_name = 'company' # name of the foreign key field


class CarCompanyAdmin(admin.ModelAdmin):
    search_fields = ['name'] # this is required for django's autocomplete functionality
    # ...


class CarModelAdmin(admin.ModelAdmin):
    list_filter = [CarCompanyFilter]
    # ...

After following these steps you may see the filter as:

  1. This is how the list filter is rendered in the form of a dropdown when the package is used
  2. And the dropdown filter is also Searchable

Features Offered:

  1. If you have large fields in Admin Filter, the sidebar gets widened, this package provides you the same list in a dropdown format, hence, no such hassle.
  2. If you have more than, say 20, field items, list filter is now a long side-pane, just ruining the admin interface. The Dropdown filter fixes that.
  3. The Dropdown is also "Searchable", with an input text field (which utilizes Django’s own auto_complete functionailty), so as long as the Django version you are using is greater than 2.0, you should be fine.
  4. You can customize other list_filters you may have, like change the Title above the dropdown, or a custom Search logic etc.
  5. You can customize Widget Texts to display in the Dropdown Option to use something other than the default str(obj)

0👍

To add search functionality to @beholderrk and @Gediminas solution use the follow

Create searchable_dropdown_filter.html in templates/admin folder and paste the following code

{% load i18n %}
{% load static %}

<!-- Django's Select2 CSS -->
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/vendor/select2/select2.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/autocomplete.css' %}">

<!-- jQuery (Django Admin uses this, so you can refer to it) -->
<script src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>

<!-- Django's Select2 JS -->
<script src="{% static 'admin/js/vendor/select2/select2.full.js' %}"></script>

<script type="text/javascript">
    $(document).ready(function() {
        $('.searchable-select').select2();
    });

    var go_from_select = function(opt) { window.location = window.location.pathname + opt };
</script>


<h3>{{ title }}</h3>
<ul class="admin-filter-{{ title|cut:' ' }}">
{% if choices|slice:"4:" %}
    <li>
    <select style="width: 95%;"
        onchange="go_from_select(this.options[this.selectedIndex].value)"
        class="searchable-select"
    >
    {% for choice in choices %}
        <option{% if choice.selected %} selected="selected"{% endif %}
         value="{{ choice.query_string|iriencode }}">{{ choice.display }}</option>
    {% endfor %}
    </select>
    </li>
{% else %}

    {% for choice in choices %}
            <li{% if choice.selected %} class="selected"{% endif %}>
            <a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li>
    {% endfor %}

{% endif %}
</ul>

In your admin.py or filters.py paste the following

from django.contrib.admin.filters import RelatedFieldListFilter

class SearchableDropdownFilter(RelatedFieldListFilter):
    template = 'admin/searchable_dropdown_filter.html'

In your admin implementation use it as follows

class SomeAdmin(admin.ModelAdmin):
    # ...
    list_filter = (('country', SearchableDropdownFilter), ('city', SearchableDropdownFilter),)

This uses select2 to add search functionality to the dropdown. This is very handy when you have a ton of options you want to filter from.

Leave a comment