Ordering Guide - djadmin-filters¶
Plugin: djadmin-filters
Version: 1.0.0
Last Updated: October 31, 2025
Overview¶
The djadmin-filters plugin provides sortable column headers with visual indicators (↕️ ↑ ↓). Ordering is configured directly on Column objects and supports custom labels, multi-field ordering, and related field sorting.
Prerequisites¶
djadmin-filtersplugin installed and configured- See Configuration Guide for setup
Basic Ordering¶
Enable Ordering¶
Use order=True to make a column sortable:
from djadmin import Column
Column('name', order=True)
# Clicking header cycles: neutral (↕) → ascending (↑) → descending (↓) → neutral
Disable Ordering¶
Explicitly disable ordering for a column:
Simple Example¶
from djadmin import ModelAdmin, register, Column
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name', order=True), # Sortable
Column('price', order=True), # Sortable
Column('description', order=False), # Not sortable
]
Sort Cycle¶
When a column is sortable, clicking the header cycles through three states:
- Neutral (↕) - No sorting applied, default ordering
- Ascending (↑) - Sort A-Z, 0-9, oldest-newest
- Descending (↓) - Sort Z-A, 9-0, newest-oldest
Clicking again returns to neutral (clears sorting).
Custom Labels¶
Provide custom labels for ascending and descending states:
from djadmin.dataclasses import Order
Column('price',
order=Order(
label='Price (low to high)',
descending_label='Price (high to low)'
))
These labels appear in tooltips or accessibility text, helping users understand what clicking will do.
Multi-Field Ordering¶
Order by multiple fields using the fields parameter:
Basic Multi-Field¶
Column('full_name',
order=Order(fields=['last_name', 'first_name']))
# Clicking "Full Name" orders by last name, then first name
Related Fields¶
Column('author',
order=Order(fields=['author__last_name', 'author__first_name']))
# Orders by author's last name, then first name
Complex Ordering¶
Column('availability',
order=Order(fields=['in_stock', '-stock_count', 'price']))
# Orders by: in_stock (asc), stock_count (desc), price (asc)
Related Field Ordering¶
Sort by fields on related models using Django's __ syntax:
Foreign Key¶
Reverse Foreign Key¶
# In AuthorAdmin
Column('book_count',
order=Order(fields=['books__count']))
# Sort authors by number of books
Through Multiple Relations¶
Column('publisher',
order=Order(fields=['category__publisher__name']))
# Sort through multiple relationships
Computed Field Ordering¶
Order by annotated/computed fields:
from django.db.models import Count, F
@register(Author)
class AuthorAdmin(ModelAdmin):
list_display = [
Column('name', order=True),
Column('book_count',
order=Order(fields=['book_count'])),
]
def get_queryset(self, request):
return super().get_queryset(request).annotate(
book_count=Count('books')
)
URL Parameters¶
Ordering uses the ordering query parameter:
Ascending¶
Descending¶
Multiple Fields¶
Combined with Filters¶
?category=1&ordering=-price&search=laptop
# Filter by category, search, and sort by price descending
Order Indicators¶
Visual Icons¶
The plugin displays sort icons in column headers:
- ↕ - Neutral (sortable, not currently sorting)
- ↑ - Ascending (currently sorting A-Z)
- ↓ - Descending (currently sorting Z-A)
Icon Customization¶
See UI Customization Guide for custom icon templates.
Examples¶
Basic Sortable Columns¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name', order=True),
Column('price', order=True),
Column('stock', order=True),
Column('description', order=False), # Not sortable
]
Custom Sort Labels¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name',
order=Order(
label='Name (A-Z)',
descending_label='Name (Z-A)'
)),
Column('price',
order=Order(
label='Price (low to high)',
descending_label='Price (high to low)'
)),
Column('created_at',
order=Order(
label='Oldest first',
descending_label='Newest first'
)),
]
Multi-Field Ordering¶
@register(Person)
class PersonAdmin(ModelAdmin):
list_display = [
Column('full_name',
order=Order(fields=['last_name', 'first_name'])),
Column('address',
order=Order(fields=['city', 'state', 'zip_code'])),
]
Related Field Ordering¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name', order=True),
Column('category',
order=Order(fields=['category__name'])),
Column('manufacturer',
order=Order(fields=['manufacturer__company_name'])),
]
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'category', 'manufacturer'
)
Computed Field Ordering¶
from django.db.models import Count, Avg
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name', order=True),
Column('review_count',
label='Reviews',
order=Order(fields=['review_count'])),
Column('avg_rating',
label='Avg. Rating',
order=Order(fields=['avg_rating'])),
]
def get_queryset(self, request):
return super().get_queryset(request).annotate(
review_count=Count('reviews'),
avg_rating=Avg('reviews__rating')
)
Default Ordering¶
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('name', order=True),
Column('price', order=True),
Column('created_at', order=True),
]
# Default ordering when no ?ordering parameter
ordering = ['-created_at', 'name']
Complete Example¶
from django.db.models import Count, Avg, F
from djadmin import ModelAdmin, register, Column
from djadmin.dataclasses import Order
from myapp.models import Product
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
# Simple ordering
Column('name',
order=Order(
label='Name (A-Z)',
descending_label='Name (Z-A)'
)),
# Related field ordering
Column('category',
order=Order(fields=['category__name'])),
# Price ordering with custom labels
Column('price',
order=Order(
label='Price (low to high)',
descending_label='Price (high to low)'
)),
# Computed field ordering
Column('review_count',
label='Reviews',
order=Order(fields=['review_count'])),
# Multi-field ordering
Column('full_name',
order=Order(fields=['last_name', 'first_name'])),
# Not sortable
Column('description', order=False),
]
# Default ordering
ordering = ['-created_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'category'
).annotate(
review_count=Count('reviews'),
avg_rating=Avg('reviews__rating')
)
Best Practices¶
Use Natural Sort Orders¶
# Good - intuitive sorting
Column('price',
order=Order(
label='Price (low to high)',
descending_label='Price (high to high)'
))
# Avoid - confusing direction
Column('price', order=True) # Unclear what "ascending" means
Optimize Related Field Queries¶
# Good - uses select_related to avoid N+1 queries
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
Column('category', order=Order(fields=['category__name'])),
]
def get_queryset(self, request):
return super().get_queryset(request).select_related('category')
Annotate Before Ordering Computed Fields¶
# Good - annotate in get_queryset
def get_queryset(self, request):
return super().get_queryset(request).annotate(
review_count=Count('reviews')
)
list_display = [
Column('review_count', order=Order(fields=['review_count'])),
]
# Avoid - ordering by unannotated field (will error)
Provide Clear Labels for Multi-Field Ordering¶
# Good - label explains the sort behavior
Column('full_name',
label='Full Name',
order=Order(
fields=['last_name', 'first_name'],
label='Sort by last name, then first name'
))
# Avoid - unclear what fields are used
Column('full_name', order=Order(fields=['last_name', 'first_name']))
Troubleshooting¶
Ordering Not Working¶
Problem: Column header shows sort icon but clicking doesn't sort
Solutions:
1. Check order=True or order=Order(...) is set
2. Verify field exists on model
3. Check ordering URL parameter appears after clicking
4. Inspect queryset with Django Debug Toolbar
N+1 Query Issues¶
Problem: Sorting by related field causes many database queries
Solutions:
1. Use select_related() for ForeignKey fields:
- Use
prefetch_related()for ManyToMany fields:
Computed Field Not Sortable¶
Problem: Ordering by computed field causes error
Solutions:
1. Annotate the field in get_queryset():
def get_queryset(self, request):
return super().get_queryset(request).annotate(
review_count=Count('reviews')
)
- Match the annotation name to the order field name:
Sort Icon Not Showing¶
Problem: Column should be sortable but no icon appears
Solutions:
1. Verify order=True is set
2. Check CSS is loading correctly
3. Clear browser cache
4. Inspect HTML for sort icon element
See Also¶
- Configuration Guide - Plugin setup and installation
- Filtering Guide - Column filtering configuration
- Advanced Usage - Performance optimization
- UI Customization - Custom sort icons
- Django QuerySet Reference - order_by() documentation