Skip to main content

Overview

Dropdown filters provide a clean, space-efficient way to filter data using select dropdowns. They’re ideal for fields with a limited number of options and support both single and multiple selections.

Available Dropdown Filters

DropdownFilter

Custom filter with single selection dropdown

MultipleDropdownFilter

Custom filter with multiple selection support

ChoicesDropdownFilter

Automatic filter for fields with choices

MultipleChoicesDropdownFilter

Multiple selection for choice fields

RelatedDropdownFilter

Single selection for foreign key relationships

MultipleRelatedDropdownFilter

Multiple selection for foreign key relationships
Create custom filters with dropdown UI using SimpleListFilter logic.

Basic Example

admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import DropdownFilter

from .models import Product


class StatusFilter(DropdownFilter):
    title = "Status"
    parameter_name = "status"

    def lookups(self, request, model_admin):
        return (
            ("draft", "Draft"),
            ("published", "Published"),
            ("archived", "Archived"),
        )

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(status=self.value())
        return queryset


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [StatusFilter]

Removing “All” Option

By default, dropdowns include an “All” option to clear the filter. You can remove it:
admin.py
class StatusFilter(DropdownFilter):
    title = "Status"
    parameter_name = "status"
    all_option = None  # Remove "All" option

    def lookups(self, request, model_admin):
        return (
            ("draft", "Draft"),
            ("published", "Published"),
        )

Custom “All” Label

admin.py
class StatusFilter(DropdownFilter):
    title = "Status"
    parameter_name = "status"
    all_option = ["", "Any Status"]  # Custom label

    def lookups(self, request, model_admin):
        return (
            ("active", "Active"),
            ("inactive", "Inactive"),
        )

MultipleDropdownFilter

Allow users to select multiple values from the dropdown.
admin.py
from unfold.contrib.filters.admin import MultipleDropdownFilter


class TagsFilter(MultipleDropdownFilter):
    title = "Tags"
    parameter_name = "tags"

    def lookups(self, request, model_admin):
        return (
            ("featured", "Featured"),
            ("new", "New"),
            ("sale", "On Sale"),
        )

    def queryset(self, request, queryset):
        # self.value() returns a list when multiple items selected
        if self.value():
            return queryset.filter(tags__name__in=self.value())
        return queryset


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [TagsFilter]
With MultipleDropdownFilter, the value() method returns a list of selected values, not a single value.

ChoicesDropdownFilter

Automatically create dropdown filters for model fields with choices defined.
models.py
from django.db import models


class Product(models.Model):
    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        PUBLISHED = "published", "Published"
        ARCHIVED = "archived", "Archived"

    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.DRAFT,
    )
admin.py
from unfold.contrib.filters.admin import ChoicesDropdownFilter


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [
        ("status", ChoicesDropdownFilter),  # Tuple format
    ]
Use the tuple format (field_name, FilterClass) when applying filters to model fields.

MultipleChoicesDropdownFilter

Multiple selection version of ChoicesDropdownFilter.
admin.py
from unfold.contrib.filters.admin import MultipleChoicesDropdownFilter


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [
        ("status", MultipleChoicesDropdownFilter),
    ]

RelatedDropdownFilter

Create dropdown filters for foreign key relationships.
models.py
class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Product(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
admin.py
from unfold.contrib.filters.admin import RelatedDropdownFilter


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [
        ("category", RelatedDropdownFilter),
    ]
You can override the queryset to limit which related objects appear:
admin.py
from unfold.contrib.filters.admin import RelatedDropdownFilter


class ActiveCategoryFilter(RelatedDropdownFilter):
    def field_choices(self, field, request, model_admin):
        # Only show active categories
        return field.get_choices(
            include_blank=False,
            limit_choices_to={"is_active": True}
        )


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [
        ("category", ActiveCategoryFilter),
    ]

MultipleRelatedDropdownFilter

Allow filtering by multiple related objects.
admin.py
from unfold.contrib.filters.admin import MultipleRelatedDropdownFilter


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_filter = [
        ("category", MultipleRelatedDropdownFilter),
    ]
This is particularly useful for many-to-many relationships where users might want to filter by multiple related objects.

Faceted Counts

All dropdown filters support showing counts when facets are enabled:
admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import RelatedDropdownFilter


@admin.register(Product)
class ProductAdmin(ModelAdmin):
    show_facets = admin.ShowFacets.ALWAYS
    list_filter = [
        ("category", RelatedDropdownFilter),  # Shows "Electronics (42)"
    ]

Advanced Customization

Custom Form Class

You can provide your own form class for rendering:
admin.py
from unfold.contrib.filters.forms import DropdownForm


class CustomDropdownForm(DropdownForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Add custom attributes or classes
        self.fields['filter'].widget.attrs.update({
            'class': 'custom-dropdown',
        })


class StatusFilter(DropdownFilter):
    form_class = CustomDropdownForm
    title = "Status"
    parameter_name = "status"

Dynamic Lookups

Generate filter options dynamically based on database content:
admin.py
class DynamicCategoryFilter(DropdownFilter):
    title = "Category"
    parameter_name = "category"

    def lookups(self, request, model_admin):
        # Get categories that have products
        categories = Category.objects.filter(
            product__isnull=False
        ).distinct().order_by('name')
        
        return [(cat.id, cat.name) for cat in categories]

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(category_id=self.value())
        return queryset

API Reference

title
str
required
Display name for the filter in the admin
parameter_name
str
required
URL parameter name for the filter value
all_option
list[str] | None
default:"[\"\", \"All\"]"
Option to clear filter. Set to None to remove
form_class
type[DropdownForm]
default:"DropdownForm"
Form class used for rendering the dropdown
template
str
default:"\"unfold/filters/filters_field.html\""
Template used to render the filter

Methods

lookups
method
def lookups(self, request, model_admin):
    return ()
Returns a list of tuples (value, label) for filter options.
queryset
method
def queryset(self, request, queryset):
    return queryset
Filters the queryset based on the selected value.
value
method
def value(self):
    return self.used_parameters.get(self.parameter_name)
Returns the currently selected filter value.

Best Practices

  • Best for: 5-50 options
  • Avoid for: >100 options (use autocomplete instead)
  • Good for: Status fields, categories, types
  • Use select_related() or prefetch_related() in get_queryset() for related filters
  • Cache dynamic lookups when possible
  • Consider AutocompleteSelectFilter for large datasets
  • Keep labels short and descriptive
  • Order options logically (alphabetically, by frequency, etc.)
  • Use multiple filters for complex filtering needs

Next Steps

Autocomplete Filters

Learn about AJAX-powered autocomplete for large datasets

Checkbox & Radio Filters

Explore alternative selection UI patterns

Build docs developers (and LLMs) love