Across 8 indexed models and the bundled help documentation. Free-text query with phrase, prefix, exclusion, and boolean operators — same syntax across SQLite FTS5 and Postgres FTS.
ANDrefunding, refundedapi, excludes slowFrom anywhere else on the site, press Ctrl + K (or ⌘ + K) to pop up the quick search overlay — a smaller version of this page. This page itself is the deeper experience.
Every hit on this page is a title, a line of context and a link. When a model
deserves more, give its CRUDView a transform_hit: whatever the dict
returns beyond display and subtitle travels with the hit
as extra — to this page, the omnibar, /api/search/ and the
MCP tool alike. The blog does exactly this, so a published post renders as an
article card instead of a row.
# apps/blog/views.py class PostCRUDView(CRUDView): enable_search = True search_fields = ["title", "excerpt", "body_markdown"] def get_search_variants(self): return {"default": "Article card", "compact": "Title + permalink"} def transform_hit(self, post, variant="default"): if variant == "compact": return {"display": post.title, "url": post.public_url} return { "display": post.title, "subtitle": post.excerpt, "card": "article", # the template switches on this "blog": post.blog.name, "published_at": post.published_at, "reading_minutes": words // 220, "tags": [t.name for t in post.tags.all()], "cover_url": post.cover_image.url, }
The page then branches on hit.extra.card and renders this instead of
the default row:
The compact variant also becomes its own MCP tool,
search_blog_posts_compact, for agents that only need a permalink.
Template: templates/website/includes/search_article_card.html.
Reference: search docs.
titleexcerptbody_markdown
Choose the color mode for your app.
The accent color for your app.
Choose the font family that fits your app.
Choose the gray shade for your app.
Choose the border radius factor for your app.
Choose the page layout for your app.