Configuration Guide - djadmin-filters¶
Plugin: djadmin-filters
Version: 1.0.0
Last Updated: October 31, 2025
Overview¶
The djadmin-filters plugin provides filtering, ordering, and search capabilities for django-admin-deux using django-filter integration. It offers a modern column-centric configuration approach while maintaining backward compatibility with Django admin's list_filter pattern.
Prerequisites¶
- Python 3.11+
- Django 5.2+ (uses
{% querystring %}tag introduced in Django 5.0) - django-admin-deux (core package)
- django-filter >=23.0
Installation¶
Option 1: Install with django-admin-deux¶
Install the filters plugin along with django-admin-deux:
Option 2: Install separately¶
Install the plugin as a standalone package:
Django Settings¶
Add the required apps to your INSTALLED_APPS:
# settings.py
INSTALLED_APPS = [
# ... Django apps
'djadmin', # Core package
'djadmin_filters', # This plugin
'django_filters', # Required dependency
# ... your apps
]
Important: django_filters must be in INSTALLED_APPS for widget templates to be found.
Quick Start¶
The plugin uses a column-centric configuration approach where filters and ordering are configured directly on Column objects:
# myapp/djadmin.py
from djadmin import ModelAdmin, register, Column
from djadmin.dataclasses import Filter, Order
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
# Text search filter + sortable
Column('name',
filter=Filter(lookup_expr='icontains'),
order=True),
# Range filter + sortable
Column('price',
filter=Filter(lookup_expr=['gte', 'lte']),
order=True),
# Exact match filter, not sortable
Column('category',
filter=True,
order=False),
# No filter, sortable only
Column('stock',
filter=False,
order=True),
]
That's it! The admin will now display: - A filter sidebar with inputs for name, price, and category - Sortable column headers for name, price, and stock - Visual sort indicators (↕️ ↑ ↓)
Column-Centric Configuration¶
The modern approach configures filters and ordering directly on columns:
Basic Syntax¶
Filter Configuration¶
from djadmin.dataclasses import Filter
# Simple exact match
Column('status', filter=True)
# Custom lookup expression
Column('name', filter=Filter(lookup_expr='icontains'))
# Range filter
Column('price', filter=Filter(lookup_expr=['gte', 'lte']))
# Custom widget
Column('status',
filter=Filter(
lookup_expr='exact',
widget=forms.Select(choices=[...])
))
# Method filter
Column('featured',
filter=Filter(method=my_filter_method))
Order Configuration¶
from djadmin.dataclasses import Order
# Simple ordering
Column('name', order=True)
# Custom labels
Column('price',
order=Order(
label='Price (low to high)',
descending_label='Price (high to low)'
))
# Custom fields
Column('full_name',
order=Order(fields=['last_name', 'first_name']))
Legacy Support¶
The plugin maintains backward compatibility with Django admin's list_filter attribute and introduces a new order_fields attribute:
Legacy Filtering¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = ['name', 'price', 'category']
# Old-style filtering (still works!)
list_filter = ['category', 'status']
The metaclass automatically normalizes this to:
list_display = [
Column('name'),
Column('price'),
Column('category', filter=True), # Normalized from list_filter
]
Legacy Ordering¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = ['name', 'price', 'category']
# New attribute for backward compatibility
order_fields = ['name', 'price']
Normalized to:
list_display = [
Column('name', order=True), # Normalized from order_fields
Column('price', order=True), # Normalized from order_fields
Column('category', order=False),
]
Mixing Approaches¶
You can mix column-centric and legacy configuration:
@register(Product)
class ProductAdmin(ModelAdmin):
# Column-centric for some fields
list_display = [
Column('name',
filter=Filter(lookup_expr='icontains'),
order=True),
'price', # Plain field name
'category', # Plain field name
]
# Legacy for others
list_filter = ['category', 'status']
order_fields = ['price']
The metaclass normalizes everything to Column objects.
Migration from Milestone 1¶
Breaking change: Column.sortable was removed in Milestone 2.
Before (Milestone 1):
After (Milestone 2):
Why: Consistent naming with list_filter → Column.filter and order_fields → Column.order.
Feature Advertising¶
The plugin advertises the following features:
filter- Filtering capabilitiesordering- Column sorting
These are automatically detected when you use:
- Column(..., filter=True/Filter(...))
- Column(..., order=True/Order(...))
- list_filter = [...]
- order_fields = [...]
If the plugin is not installed but you use these features, Django will raise ImproperlyConfigured at startup.
URL Parameters¶
The plugin uses standard URL query parameters that are preserved across actions:
Filtering¶
# Exact match
?category=1
# Lookup expression
?name__icontains=laptop
# Range filter
?price__gte=100&price__lte=500
Ordering¶
Combined¶
Query Parameter Preservation¶
All features automatically preserve other query parameters:
- Searching preserves filters and ordering
- Filtering preserves search and ordering
- Ordering preserves search and filters
- Pagination preserves all parameters
This is handled automatically by the {% query_params_as_hidden_inputs %} template tag.
Complete Example¶
# myapp/djadmin.py
from django import forms
from djadmin import ModelAdmin, register, Column
from djadmin.dataclasses import Filter, Order
from myapp.models import Product, Category
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
# Text search with custom label
Column('name',
label='Product Name',
filter=Filter(
lookup_expr='icontains',
label='Search by name'
),
order=True),
# Exact match with custom widget
Column('category',
filter=Filter(
lookup_expr='exact',
widget=forms.Select(choices=Category.choices)
),
order=False),
# Range filter with custom order labels
Column('price',
filter=Filter(lookup_expr=['gte', 'lte']),
order=Order(
label='Price (low to high)',
descending_label='Price (high to low)'
)),
# Simple filter + order
Column('stock',
filter=True,
order=True),
# Display only, no filter or order
Column('description',
filter=False,
order=False),
]
# Optimize queries
def get_queryset(self, request):
return super().get_queryset(request).select_related('category')
Troubleshooting¶
Filters not showing¶
-
Check
django_filtersis inINSTALLED_APPS -
Check filter configuration
-
Check browser console for JavaScript errors
Ordering not working¶
-
Check column has
order=True -
Check field exists on model
-
Check
orderingURL parameter is present
Widget templates not found¶
This error means django_filters is not in INSTALLED_APPS. Add it:
INSTALLED_APPS = [
'djadmin',
'djadmin_filters',
'django_filters', # Required for widget templates
]
Next Steps¶
- Filtering Guide - Detailed filtering configuration
- Ordering Guide - Detailed ordering configuration
- Advanced Usage - Custom FilterSets, performance optimization
- UI Customization - Template overrides, custom icons
See Also¶
- Getting Started - django-admin-deux quick start
- Column API - Column configuration reference
- django-filter Documentation - Underlying library