Search¶
Django Cast supports full-text search and faceted filtering on blog and podcast
list pages. Filtering is handled by PostFilterset (in cast.filters) and
is available in two contexts:
Blog/podcast list pages (Wagtail page views)
Wagtail API pages listing when
use_post_filter=true
Filter Parameters¶
The filter parameter names are shared between UI routes and API routes.
search: full-text search via Django Cast’s modelsearch wrapperdate_afteranddate_before: date range filter forvisible_date(from thedatefilter)date_facets: single month facet, formatYYYY-MMcategory_facets: category slugtag_facets: tag slugo: ordering forvisible_date(visible_dateor-visible_date)
Default configuration:
CAST_FILTERSET_FACETS = [
"search",
"date",
"date_facets",
"category_facets",
"tag_facets",
"o",
]
Search and Filter Examples¶
Blog list routes (example slug styleguide-blog):
/styleguide-blog/?search=python
/styleguide-blog/?date_after=2026-01-01&date_before=2026-12-31
/styleguide-blog/?date_facets=2026-02&tag_facets=django
/styleguide-blog/?category_facets=til&o=-visible_date
Wagtail API pages endpoint (if cast is mounted at /cast/):
/cast/api/wagtail/pages/?type=cast.Post&child_of=4&use_post_filter=true&search=python
/cast/api/wagtail/pages/?type=cast.Post&child_of=4&use_post_filter=true&date_facets=2026-02
Facet Behavior¶
Search input is normalized before it reaches modelsearch. Null bytes are stripped, repeated whitespace/hyphen runs are collapsed, leading/trailing whitespace is removed, and very long values are capped. Malformed or punctuation-only public searches return no results instead of raising a server error.
In the default (legacy) mode, facet counts reflect the currently filtered queryset. The
?mode=modalAPI uses a different counting strategy; see Conjunctive vs Disjunctive Faceting for details.Tag/category facet options with count
0are omitted from the standard filterset choices.Date facets are month buckets generated from
visible_date.Invalid facet values are ignored: -
date_facetsmust parse asYYYY-MM. -tag_facetsandcategory_facetsmust pass Django slug validation.Generated facet links intentionally drop
pagefrom the query string to avoid broken pagination URLs.
Conjunctive vs Disjunctive Faceting¶
django-cast provides two facet API modes. The default (legacy) mode returns
counts matching all currently active filters. The modal mode (?mode=modal)
adjusts counts per facet group so a UI can show “what happens if I switch this
facet value next?”.
Both modes are documented in Modal Facet API.
These correspond to two counting strategies common in faceted navigation.
Conjunctive faceting¶
Counts are computed from the fully filtered result set (all active filters applied).
This is the strategy used by the default (legacy) facet API.
Disjunctive faceting¶
For a given facet group, counts are computed with that group temporarily excluded, while keeping the other active filters.
This is the strategy used by the modal facet API (?mode=modal).
Practical example¶
Assume posts:
Post A:
category=til,tag=pythonPost B:
category=til,tag=djangoPost C:
category=weeknotes,tag=python
Active URL state:
/styleguide-blog/?category_facets=til&tag_facets=python
Conjunctive tag counts (legacy):
Current result set is only Post A.
Tag options from that set are effectively
python (1).djangois not available until you first clear the tag filter.
Disjunctive tag counts (modal):
Keep
category_facets=tilbut exclude the current tag filter while counting tag options.Eligible posts are Post A and Post B.
Tag options become
python (1)anddjango (1).Clicking
djangoswitches directly to:
/styleguide-blog/?category_facets=til&tag_facets=django
Why django-cast has both¶
Legacy mode keeps backward-compatible behavior for existing clients.
mode=modalsupports one-click switching inside a facet group (without a clear-first step).The total result count still reflects all active filters combined.
Each facet group’s option counts answer: “if I change only this group, how many results would I get?”.
See API Documentation for exact response fields (
result_count,all_count,options).
Modal Facet API¶
For modal UIs, use /cast/api/facet_counts/<blog_id>/?mode=modal (path prefix depends on how you include cast.urls).
Modal payloads are calculated by cast.modal_facet_counts.get_modal_facet_counts and return:
result_countfor the fully selected statePer-group
all_countwith that group temporarily excludedoptionsincluding zero-count values so the modal can keep the full facet universe visible
See API Documentation for exact response shapes.
Architecture Notes¶
For the end-to-end request/data flow (blog queryset, PostFilterset, legacy serializer path, and modal API path), see Search and Facet Flow.