How to add a dropdown list in ModelAdmin list_filter

Hello,
this is my first post but I use django for some years. I tried to search this question without finding results.
I have a small app of type “Countries => City” as reported below:

# models.py
class Country(models.Model):
	name = models.CharField(max_length=150, help_text="Country name")
	iso2 = models.CharField(max_length=2, help_text="Country ISO2")
	iso3 = models.CharField(max_length=3, help_text="Country ISO3")

class City(models.Model):
	name = models.CharField(max_length=150, help_text="City name")
	country = models.ForeignKey('Country', on_delete=models.SET_NULL, null=True, blank=True)

and in admin.py I set City list_filter as below:

@admin.register(City)
class CityAdmin(admin.ModelAdmin):
	list_display = ('id', 'name')
	list_filter = ('country__name',)

which show all countries in right menu.
So, is there a way to show list of countries in a dropdown menu instead of showing them as a list?

Thanks

In the standard django-admin i don’t know a way of achieving this.
I use jazzmin on my projects, it has this feature builtin. It’s a great project, and it’s beautiful. In their docs there’s a link to a live preview.

Hi, thank you for your answer!
I forgot to specify that I would not like to use third-parties libraries

Oh, i see.

Looking into the django source code, i think that you need to:

  • Create a filter class that inherits from django.contrib.admin.filters.SimpleListFilter;
  • override the template_name to your template;
  • Use the created filter on your admin class;

The default template that django uses is at: django/contrib/admin/templates/admin/filter.html. It’s contents are below:

{% load i18n %}
<details data-filter-title="{{ title }}" open>
  <summary>
    {% blocktranslate with filter_title=title %} By {{ filter_title }} {% endblocktranslate %}
  </summary>
  <ul>
  {% for choice in choices %}
    <li{% if choice.selected %} class="selected"{% endif %}>
    <a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li>
  {% endfor %}
  </ul>
</details>

So instead of creating a <ul> and <li> for each option, you may create a <select> and give it the option for each option.
Hope you suceed!

I took inspiration from this link and it works!

This is my filter template put in <APP_NAME>/<MODEL_NAME>/filter.html:

{% load i18n %}
<script type="text/javascript">var go_from_select = function(opt) { window.location = window.location.pathname + opt };</script>
<h3>{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}</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>

I hope it will be useful for someone!

1 Like