`queryset()` is the working half of a Django Admin `SimpleListFilter`: it receives the current request and a `QuerySet`, then returns the `QuerySet` that should populate the changelist. `lookups()` supplies the links people can choose; `queryset()` turns a chosen value into a database filter. In short, one method names the menu, and the other does the actual filtering. section 5.2.2
The method should preserve the unfiltered results when no filter choice is active. Read the selected query-parameter value with `self.value()`. If it is `None`, return the incoming `queryset` unchanged. Otherwise, convert the selected value as needed, call `filter()` on the incoming `QuerySet`, and return the result. That incoming object represents database results, rather than an ordinary Python list. section 5.2.2 section 1.1.2
Filter musicians by decade of birth
A `DecadeListFilter` uses `parameter_name = 'decade'` and offers decade-start values such as `1940`. Its `queryset()` method gets `start = self.value()`. With no value, it returns `queryset`. With a value, it converts the string to an integer and returns `queryset.filter(birth__gte=date(start, 1, 1), birth__lte=date(start + 9, 12, 31))`, limiting the admin listing to people born during that decade. section 5.2.2
Keep the filter choices practical. A field with a small, fixed set of values can use a complete list, but showing every value from a broad field such as a last name is ugly and can hurt performance. For large open-ended data, use search instead of making `queryset()` support a giant sidebar list. section 5.2.2
Listing 5.4 Custom list filter based on decades
# RiffMates/bands/admin.py
...
from datetime import datetime, date #1
...
class DecadeListFilter(admin.SimpleListFilter): #2
title = 'decade born' #3
parameter_name = 'decade' #4
def lookups(self, request, model_admin):
result = []
this_year = datetime.today().year
this_decade = (this_year // 10) * 10 #5
start = this_decade - 10
for year in range(start, start - 100, -10): #6
result.append( (str(year), f"{year}-{year+9}") ) #7
return result
def queryset(self, request, queryset):
start = self.value() #8
if start is None:
return queryset
start = int(start)
result = queryset.filter(
birth__gte=date(start, 1, 1), #9
birth__lte=date(start + 9, 12, 31),
)
return result
...
class MusicianAdmin(admin.ModelAdmin):
list_display = ("id", "last_name", "first_name", "birth", #10
"show_weekday", )
list_filter = (DecadeListFilter, ) #11
Inspect the complete custom filter to see how `lookups()` creates decade choices and how `queryset()` handles an absent selection before applying date-range constraints. section 5.2.2
