Filtering Guide - djadmin-filters¶
Plugin: djadmin-filters
Version: 1.0.0
Last Updated: October 31, 2025
Overview¶
The djadmin-filters plugin provides column-based filtering using django-filter with a sidebar UI. Filters are configured directly on Column objects and support various lookup expressions, custom widgets, and method filters.
Prerequisites¶
djadmin-filtersplugin installed and configureddjango_filtersinINSTALLED_APPS- See Configuration Guide for setup
Basic Filtering¶
Enable Filtering¶
Use filter=True for simple exact-match filters:
from djadmin import Column
Column('category', filter=True)
# Generates: <input name="category" type="text">
Disable Filtering¶
Explicitly disable filtering for a column:
Simple Example¶
from djadmin import ModelAdmin, register, Column
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name', filter=True), # Exact match filter
Column('category', filter=True), # Exact match filter
Column('stock', filter=True), # Exact match filter
]
Lookup Expressions¶
Use the Filter dataclass to specify lookup expressions:
from djadmin import Column
from djadmin.dataclasses import Filter
Column('name', filter=Filter(lookup_expr='icontains'))
Common Lookup Expressions¶
Text Searches¶
# Contains (case-sensitive)
Column('name', filter=Filter(lookup_expr='contains'))
# Contains (case-insensitive) - Most common for text search
Column('name', filter=Filter(lookup_expr='icontains'))
# Exact match (case-sensitive)
Column('sku', filter=Filter(lookup_expr='exact'))
# Exact match (case-insensitive)
Column('code', filter=Filter(lookup_expr='iexact'))
# Starts with
Column('name', filter=Filter(lookup_expr='startswith'))
Column('name', filter=Filter(lookup_expr='istartswith')) # Case-insensitive
# Ends with
Column('email', filter=Filter(lookup_expr='endswith'))
Column('email', filter=Filter(lookup_expr='iendswith')) # Case-insensitive
Numeric Comparisons¶
# Greater than or equal
Column('price', filter=Filter(lookup_expr='gte'))
# Greater than
Column('price', filter=Filter(lookup_expr='gt'))
# Less than or equal
Column('price', filter=Filter(lookup_expr='lte'))
# Less than
Column('price', filter=Filter(lookup_expr='lt'))
Range Filters¶
Use a list of lookup expressions for range filters:
# Price range (min/max)
Column('price', filter=Filter(lookup_expr=['gte', 'lte']))
# Generates:
# <input name="price__gte" placeholder="Min">
# <input name="price__lte" placeholder="Max">
# Date range
Column('created_at', filter=Filter(lookup_expr=['gte', 'lte']))
# Generates:
# <input type="date" name="created_at__after">
# <input type="date" name="created_at__before">
Other Lookup Expressions¶
# In list
Column('status', filter=Filter(lookup_expr='in'))
# Is null / Is not null
Column('deleted_at', filter=Filter(lookup_expr='isnull'))
Complete Lookup Reference¶
All Django field lookup expressions are supported:
| Expression | Description | Example |
|---|---|---|
exact |
Exact match (case-sensitive) | ?sku=ABC123 |
iexact |
Exact match (case-insensitive) | ?code=abc |
contains |
Contains substring | ?name__contains=laptop |
icontains |
Contains substring (case-insensitive) | ?name__icontains=laptop |
startswith |
Starts with | ?name__startswith=Pro |
istartswith |
Starts with (case-insensitive) | ?name__istartswith=pro |
endswith |
Ends with | ?email__endswith=@example.com |
iendswith |
Ends with (case-insensitive) | ?email__iendswith=@example.com |
gt |
Greater than | ?price__gt=100 |
gte |
Greater than or equal | ?price__gte=100 |
lt |
Less than | ?price__lt=500 |
lte |
Less than or equal | ?price__lte=500 |
in |
In list | ?status__in=active,pending |
isnull |
Is null | ?deleted_at__isnull=True |
range |
Range | ?price__range=100,500 |
date |
Date match | ?created_at__date=2025-10-31 |
year |
Year match | ?created_at__year=2025 |
month |
Month match | ?created_at__month=10 |
day |
Day match | ?created_at__day=31 |
Custom Widgets¶
Provide custom Django form widgets for filter inputs:
Select Widget¶
from django import forms
from djadmin.dataclasses import Filter
Column('status',
filter=Filter(
lookup_expr='exact',
widget=forms.Select(choices=[
('', '-- All --'),
('active', 'Active'),
('inactive', 'Inactive'),
('archived', 'Archived'),
])
))
Checkbox Widget¶
Date Widget¶
Column('published_date',
filter=Filter(
lookup_expr='gte',
widget=forms.DateInput(attrs={'type': 'date'})
))
Number Widget¶
Column('stock',
filter=Filter(
lookup_expr='lte',
widget=forms.NumberInput(attrs={
'min': 0,
'placeholder': 'Max stock'
})
))
Multiple Select Widget¶
Method Filters¶
Use custom filter methods for complex logic:
Basic Method Filter¶
from djadmin.dataclasses import Filter
def filter_is_featured(queryset, name, value):
"""Custom filter method."""
if value:
return queryset.filter(featured=True, published=True)
return queryset
Column('featured',
filter=Filter(method=filter_is_featured))
Method Filter with Widget¶
def filter_price_range(queryset, name, value):
"""Filter by price range category."""
ranges = {
'budget': (0, 50),
'mid': (50, 200),
'premium': (200, 1000),
'luxury': (1000, float('inf')),
}
if value in ranges:
min_price, max_price = ranges[value]
return queryset.filter(price__gte=min_price, price__lt=max_price)
return queryset
Column('price_range',
label='Price Range',
filter=Filter(
method=filter_price_range,
widget=forms.Select(choices=[
('', '-- All --'),
('budget', 'Budget (< $50)'),
('mid', 'Mid-range ($50-$200)'),
('premium', 'Premium ($200-$1000)'),
('luxury', 'Luxury ($1000+)'),
])
))
Method Filter Signature¶
Method filters must accept three arguments:
def my_filter_method(queryset, name, value):
"""
Args:
queryset: Current queryset
name: Field name (usually ignored)
value: User-provided filter value
Returns:
Filtered queryset
"""
# Your filtering logic here
return queryset.filter(...)
Filter Labels¶
Override the filter label displayed in the sidebar:
Column Label vs Filter Label¶
Column('price',
label='Price', # Column header label
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Price Range' # Filter label in sidebar
))
Range Filter Labels¶
Column('price',
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Price Range',
min_label='Minimum Price',
max_label='Maximum Price'
))
# Generates inputs with custom placeholders
Filter Examples¶
Text Search Filter¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name',
filter=Filter(
lookup_expr='icontains',
label='Search Products'
)),
]
Category Filter with Dropdown¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('category',
filter=Filter(
lookup_expr='exact',
widget=forms.Select(choices=Category.choices)
)),
]
Price Range Filter¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('price',
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Price Range'
)),
]
Date Range Filter¶
@register(Order)
class OrderAdmin(ModelAdmin):
list_display = [
Column('created_at',
label='Order Date',
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Date Range'
)),
]
Boolean Filter¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('is_active',
label='Active',
filter=Filter(
lookup_expr='exact',
widget=forms.Select(choices=[
('', '-- All --'),
('True', 'Active'),
('False', 'Inactive'),
])
)),
]
Multi-Select Filter¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('tags',
filter=Filter(
lookup_expr='in',
widget=forms.SelectMultiple(
choices=[(t.id, t.name) for t in Tag.objects.all()]
)
)),
]
Complete Example¶
from django import forms
from djadmin import ModelAdmin, register, Column
from djadmin.dataclasses import Filter
from myapp.models import Product, Category
def filter_in_stock(queryset, name, value):
"""Filter products that are in stock."""
if value:
return queryset.filter(stock__gt=0)
return queryset
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
# Text search
Column('name',
label='Product Name',
filter=Filter(
lookup_expr='icontains',
label='Search by name'
)),
# Dropdown selection
Column('category',
filter=Filter(
lookup_expr='exact',
widget=forms.Select(
choices=[('', '-- All --')] + Category.choices
)
)),
# Range filter
Column('price',
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Price Range'
)),
# Method filter
Column('in_stock',
label='In Stock',
filter=Filter(
method=filter_in_stock,
widget=forms.CheckboxInput()
)),
# Date range
Column('created_at',
label='Created',
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Created Date'
)),
]
Best Practices¶
Use Case-Insensitive Lookups for Text¶
# Good - works for all user input variations
Column('name', filter=Filter(lookup_expr='icontains'))
# Avoid - only matches exact case
Column('name', filter=Filter(lookup_expr='contains'))
Provide "All" Option in Dropdowns¶
# Good - allows clearing the filter
widget=forms.Select(choices=[
('', '-- All --'),
('active', 'Active'),
('inactive', 'Inactive'),
])
# Avoid - user can't clear filter
widget=forms.Select(choices=[
('active', 'Active'),
('inactive', 'Inactive'),
])
Use Range Filters for Numeric/Date Fields¶
# Good - allows min/max filtering
Column('price', filter=Filter(lookup_expr=['gte', 'lte']))
# Avoid - only exact match
Column('price', filter=Filter(lookup_expr='exact'))
Label Filters Clearly¶
# Good - clear what the filter does
Column('price',
label='Price',
filter=Filter(
lookup_expr=['gte', 'lte'],
label='Price Range'
))
# Avoid - ambiguous label
Column('price', filter=Filter(lookup_expr=['gte', 'lte']))
Troubleshooting¶
Filter Not Showing¶
Problem: Filter configured but not appearing in sidebar
Solutions:
1. Check django_filters is in INSTALLED_APPS
2. Verify filter=True or filter=Filter(...) is set
3. Check browser console for JavaScript errors
4. Clear browser cache
Filter Not Working¶
Problem: Filter appears but doesn't filter results
Solutions: 1. Check field name matches model field 2. Verify lookup expression is valid for field type 3. Check URL parameters in address bar 4. Inspect queryset in Django Debug Toolbar
Wrong Widget Type¶
Problem: Text input instead of dropdown
Solutions:
1. Explicitly set widget parameter:
See Also¶
- Configuration Guide - Plugin setup and installation
- Ordering Guide - Column sorting configuration
- Advanced Usage - Custom FilterSets, performance tips
- UI Customization - Template overrides
- django-filter Documentation - Underlying library