Skip to content

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:

pip install django-admin-deux[djadmin-filters]

Option 2: Install separately

Install the plugin as a standalone package:

pip install djadmin-filters

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

Column('field_name',
       filter=True,   # Enable filtering
       order=True)    # Enable ordering

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):

Column('name', sortable=True)
Column('description', sortable=False)

After (Milestone 2):

Column('name', order=True)
Column('description', order=False)

Why: Consistent naming with list_filterColumn.filter and order_fieldsColumn.order.

Feature Advertising

The plugin advertises the following features:

  • filter - Filtering capabilities
  • ordering - 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

# Ascending
?ordering=price

# Descending
?ordering=-price

Combined

# Filter + Order + Search (from core)
?category=1&ordering=-price&search=laptop

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

  1. Check django_filters is in INSTALLED_APPS

    INSTALLED_APPS = [
        'djadmin',
        'djadmin_filters',
        'django_filters',  # Required!
    ]
    

  2. Check filter configuration

    Column('field', filter=True)  # or Filter(...)
    

  3. Check browser console for JavaScript errors

Ordering not working

  1. Check column has order=True

    Column('field', order=True)
    

  2. Check field exists on model

  3. Check ordering URL 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

See Also