liveBook · Manning

Queryset()

As explained in Django in Action

queryset() is a SimpleListFilter method that receives a QuerySet and returns the filtered QuerySet for the changelist.

`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

How Django in Action covers it

How deep it goes
Walks through a complete decade-filter implementation, including selected-value handling, an unchanged fallback, and date-range ORM constraints.
Examples
Uses a RiffMates musician listing filtered into birth decades, including a displayed 1940s result.
What it assumes
Introduces the custom-filter roles while using Django Admin, `QuerySet`, request objects, and date values in the implementation.
What you can do after
Apply it via Django Admin by returning either the original `QuerySet` or a filtered date-range `QuerySet`.
How it connects
Connects `queryset()` to `SimpleListFilter`, `lookups()`, `QuerySet`, `ModelAdmin`, query parameters, and ORM `filter()`.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Django in Action. The section links point to the parts of the book it draws on.