Filtering
PowerCRUD's filtering support now has enough moving parts that it deserves its own guide rather than living only inside the basic setup walkthrough.
Use this page when you want to understand:
- the normal
filterset_fieldspath - queryset annotation fields in generated filters
- the default vs optional filter split
- null helpers and filter visibility
- how custom
filterset_classinteracts with the rest of the filtering UX
For the raw configuration matrix, see Configuration Options.
Start with generated filters
class ProjectCRUDView(PowerCRUDMixin, CRUDView):
# …
use_htmx = True
filterset_fields = ["owner", "status", "created_date"]
default_filterset_fields = ["owner", "status"]
What happens by default:
- With no
filterset_fields, the view renders the list immediately and ignores any query parameters exceptpage,page_size, andsort. - Setting
filterset_fieldsautomatically builds adjango-filterFilterSetfor those fields, including sensible widgets based on field type and optional HTMX attributes ifuse_htmxis True. - Model fields with
choicesrender as dropdown filters and match the selected value exactly. Text fields withoutchoicesstay as text inputs and useicontains. filterset_fieldsmay contain model field names and supported queryset annotation names.- Leave
default_filterset_fieldsunset to keep the current behavior and show every allowed filter immediately. - Set
default_filterset_fieldsto a smaller subset when some filters should be visible by default and the rest should stay behind the built-inAdd filtercontrol. - Once an optional filter is shown, it stays visible until the user explicitly removes it, even if its current value is empty.
- PowerCRUD persists optional filter visibility through the reserved
visible_filtersquery parameter, so shared URLs can still open the same optional filters explicitly. - PowerCRUD does not restore unsaved optional filter visibility after navigation unless the current URL or a saved favourite explicitly asks for those fields.
Default vs optional filters
default_filterset_fields is the key to the more compact filter layout.
- Treat
filterset_fieldsor the effective fields fromfilterset_classas the full set of allowed filters. - Treat
default_filterset_fieldsas the subset visible on first render. - Everything allowed but not included in
default_filterset_fieldsbecomes optional and is exposed through the built-inAdd filtercontrol.
Example:
class ProjectCRUDView(PowerCRUDMixin, CRUDView):
filterset_fields = [
"owner",
"status",
"created_date",
"priority",
"customer",
]
default_filterset_fields = ["owner", "status"]
In that example:
ownerandstatusare visible immediatelycreated_date,priority, andcustomerremain allowed filters- users can reveal those optional filters as needed through
Add filter
PowerCRUD validates default_filterset_fields against the effective bound filter names, not only against raw model field names.
Queryset annotation fields
Use queryset annotation fields when a database expression should behave like a first-class list/filter/sort column without becoming an editable model field.
from django.db.models import BooleanField, Case, Value, When
class BookQueueView(PowerCRUDMixin, CRUDView):
model = Book
def get_queryset(self):
"""Attach the public annotation used by PowerCRUD config."""
return super().get_queryset().annotate(
long_book=Case(
When(pages__gte=400, then=Value(True)),
default=Value(False),
output_field=BooleanField(),
)
)
fields = ["title", "author", "pages", "long_book"]
filterset_fields = ["author", "long_book"]
default_filterset_fields = ["author", "long_book"]
Rules:
- The configured name must match the public
annotate(...)keyword exactly. - PowerCRUD inspects the effective queryset and the annotation
output_field; it does not support alias/source-name mapping. - Annotation fields can appear in
fields,filterset_fields,default_filterset_fields, header help, cell tooltips, links, alignment overrides, and sorting. - Annotation fields are read-only. Do not put them in
form_fields,inline_edit_fields, orbulk_fields. - Use
propertiesfor Python-only display values that do not need queryset-backed filtering or first-class list ordering.
Null helpers
Nullable auto-generated filters behave differently by field type:
- Nullable
ForeignKeyandOneToOneFieldfilters keep a single dropdown and add anEmpty onlyoption near the top. - Nullable scalar filters such as
CharField,TextField,DateField,TimeField,IntegerField,DecimalField,FloatField, andBooleanFieldgain a separate companion... is emptyboolean select. - Companion null controls are rendered immediately after their parent auto-generated filter field in the form.
Use filter_null_fields_exclude when you want to opt specific generated filters out of those helpers.
- Use original field names from
filterset_fields, for example["birth_date", "favorite_genre"]. - Do not use generated companion names such as
birth_date__isnull. - Excluding a nullable scalar field suppresses the companion
... is emptycontrol. - Excluding a nullable relation field suppresses the merged
Empty onlyoption.
Example:
filterset_fields = ["owner", "published_date", "status"]
default_filterset_fields = ["owner"]
filter_null_fields_exclude = ["status"]
In that configuration:
ownerstays visible by default and keeps one dropdown withEmpty onlypublished_dateremains allowed, starts hidden, and gains a separatePublished date is emptycompanion filter when addedstatusremains allowed but gets no built-in null helper
Filterset parameters on the generated path
Use these when you are on the auto-generated filterset_fields path:
- Use
filter_queryset_optionsordropdown_sort_optionsto scope or sort the choices in generated dropdowns. - Toggle
m2m_filter_and_logic = Trueif many-to-many filters must match all selected values instead of the default OR behavior. - With
searchable_selects = True(default), filter select widgets are Tom Select-enhanced: single-selects become searchable dropdowns and M2M filters become searchable multi-select controls.
Auto-generated text filters use icontains by default. There is no separate declarative setting to change that lookup expression field by field. If you need custom lookup behavior such as iexact, startswith, or range-style filters, switch to a custom filterset_class.
Sorting behavior
Sorting is wired into the table headers.
- Clicking a column toggles
?sort=fieldand?sort=-fieldon the URL. - PowerCRUD applies that ordering server-side and always adds a secondary
pksort so pagination stays stable. - Properties can be sorted too, as long as the property name is listed in
properties. - Direct relation columns such as
authorsort byauthor__nameautomatically when the related model has a concretenamefield.
If a column should sort by something else, configure column_sort_fields_override:
class ProjectCRUDView(PowerCRUDMixin, CRUDView):
# …
column_sort_fields_override = {
"owner": "owner__email",
"customer": "customer__code",
}
column_sort_fields_override is an override map, not an exhaustive declaration. If a sortable list field is not present, PowerCRUD falls back to the normal default for that field.
Custom filtersets
If you need hand-crafted filters, switch to a custom filterset_class.
filterset_fields vs filterset_class
filterset_fields and filterset_class are alternative strategies.
If you set filterset_class, it takes precedence and PowerCRUD does not auto-generate filters from filterset_fields.
These settings only shape the auto-generated filterset_fields path:
filter_queryset_optionsfilter_null_fields_excludem2m_filter_and_logic- filter-side
dropdown_sort_options
These behaviors still apply to both generated and custom filtersets after the filterset exists:
default_filterset_fieldssearchable_selects- HTMX widget attrs when
use_htmx = Trueand the custom filterset exposessetup_htmx_attrs()
For custom filtersets, the recommended pattern is still to subclass HTMXFilterSetMixin when you want reactive filtering.
default_filterset_fields still works with a custom filterset. Validation happens against the effective filter names exposed by the bound form, including custom declared filter names.
If you want a generated text filter to use a different lookup than the default icontains, move that filter into a custom filterset_class.
HTMX and page behavior
HTMX is optional but recommended.
- When enabled, filter submissions post back to the list endpoint and the results replace the list region without a full reload.
- Pagination automatically resets to page 1 after each filter submit.
- Optional filter visibility travels with the current request through the reserved
visible_filtersquery parameter. Unsaved optional filter choices are not restored after leaving and returning to the list; save a favourite when a named list state should restore additional filter fields. - The filter panel does not reopen automatically after navigation. When filters are active, the filter toggle shows a filled primary funnel even while the panel is closed.
- When saved favourites are enabled, the filled primary funnel complements the favourite trigger: a filled primary heart means a clean saved favourite is selected, and a filled primary funnel means filters are currently applied.
Optional saved favourites
Saved favourites build on top of the filtering system, but they live in an optional contrib app and have their own installation and persistence concerns.
See Saved Favourites.