How to change the Django admin filter to use a dropdown instead of list?
DjangoDjango AdminDjango Problem Overview
If, for a field that you want to filter by, you have more than ~10 values, the filtering sidebar starts to be ugly and harder to use.
I'm looking for a solution to replace the <li>
with a dropdown selection (combobox) or something similar that will solve the same problem.
Django Solutions
Solution 1 - Django
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:
Solution 2 - Django
I cannot comment answers so I'll add to beholderrk's answer here.
-
create a new template called
dropdown_filter.html
or similar -
copy the code of filter.html from feincms to
dropdown_filter.html
-
create a new filter class in
filters.py
:from django.contrib.admin.filters import AllValuesFieldListFilter class DropdownFilter(AllValuesFieldListFilter): template = 'admin/dropdown_filter.html'
-
now you can use this filter in your admin class:
class SomeAdmin(admin.ModelAdmin): # ... list_filter = (('country', DropdownFilter),)
Works great!
Solution 3 - Django
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>
Solution 4 - Django
An easy option would be to use django-grappelli, which replaces all the filters with drop downs.
Solution 5 - Django
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:
-
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))
-
If it is just to display on a page, then you will output on a template tag something like this:
Solution 6 - Django
http://djangosuit.com/ also offers dropdowns for list filters.
Solution 7 - Django
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',)
Solution 8 - Django
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.
Solution 9 - Django
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),
)
Solution 10 - Django
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:
- This is how the list filter is rendered in the form of a dropdown when the package is used
- And the dropdown filter is also Searchable
Features Offered:
- 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.
- 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.
- 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. - You can customize other
list_filters
you may have, like change the Title above the dropdown, or a custom Search logic etc. - You can customize Widget Texts to display in the Dropdown Option to use something other than the default
str(obj)