Tutorial¶
This tutorial builds one search-enabled queryset from a raw string. You
describe which fields a query is allowed to touch, compile a query string
into a Django Q, and run it against a real
QuerySet. That is the whole path – most projects
never need more than this.
Describe your searchable fields¶
search_query_to_q() needs to know which fields a
query string may touch, and what kind of value each one holds – a plain
string, an enum, a date. You declare that once with a
FieldRegistry built from
FieldSpec entries; the registry is
what turns status:open into a validated lookup instead of an arbitrary
attribute access.
A registry for an Article model with a title, a body, an author, a status,
and a creation date looks like this:
from django_search_query.registry import FieldRegistry, FieldSpec
ARTICLE_REGISTRY = FieldRegistry(
specs=(
FieldSpec(name="title", kind="string"),
FieldSpec(name="body", kind="string"),
FieldSpec(name="author", kind="string"),
FieldSpec(
name="status",
kind="enum",
enum_values=("open", "draft", "closed"),
),
FieldSpec(
name="created",
kind="date",
supports_comparison=True,
supports_range=True,
),
),
)
This tutorial only queries status, so the rest of it works with a
single-field registry.
Compile a query¶
search_query_to_q takes the query string, the registry, a field map
(canonical field name to ORM lookup path), and which fields a bare term or
phrase searches by default – then returns a Q you pass straight to
.filter().
>>> from django_search_query import search_query_to_q
>>> from django_search_query.registry import FieldRegistry, FieldSpec
>>> registry = FieldRegistry(specs=(FieldSpec(name="status", kind="enum"),))
>>> search_query_to_q(
... "status:open",
... registry=registry,
... field_map={"status": "status"},
... default_fields=("title", "body"),
... )
<Q: (AND: ('status__iexact', 'open'))>
status:open matched the status field spec, so it compiled to an
__iexact lookup on the status column. Terms without a field: prefix
would instead search every path listed in default_fields.
Run it against a queryset¶
The Q above is unremarkable to Django – pass it to .filter() like any
other:
q = search_query_to_q(
"status:open",
registry=registry,
field_map={"status": "status"},
default_fields=("title", "body"),
)
Article.objects.filter(q)
This snippet is illustrative rather than an executed doctest: filtering a
real queryset needs a migrated Article table, which the docs build does not
provide. tests/test_query_examples.py pins the exact Q this status:open
call compiles to, so the claim above stays checked against real behavior.
Where to go next¶
Install – add the packages to a project and read about developmental releases.
django-search-query – the field-map dict, allowed fields, validation hooks, and the syntax the parser accepts.
django-admin-search-query – the opt-in admin integration and its JavaScript search input.