Skip to main content
Premium

Global Search

Include an admin in the application-wide fulltext search (the spotlight-style search that queries across all registered admins) by setting global_fulltext_search_enabled = True and defining search_fields. This flag does not control search within the admin's own list view — that runs unconditionally off search_fields — it only determines whether the admin's rows can appear in the global cross-admin search:

from habarico import ModelAdmin

from .models import Product


class ProductAdmin(ModelAdmin):
model = Product
global_fulltext_search_enabled = True # Include this admin in the application-wide fulltext search
search_fields = ["name", "description", "short_description"] # Fields to search in
fields = ["name", "sku", "description", "short_description", "status", "category"]

To exclude an admin from the global fulltext search, set global_fulltext_search_enabled to False (this is the default, on both the free library and habarico-enterprise):

from habarico import ModelAdmin

from .models import Product


class NoSearchProductAdmin(ModelAdmin):
model = Product
name = "nosearch_product"
global_fulltext_search_enabled = False # Exclude from the application-wide fulltext search
fields = ["name", "description"]

The frontend queries GET /admin/search/?search=…, optionally with &limit=…. A blank search returns an empty array without touching the database.

Result limit​

limit applies per admin: every admin with global_fulltext_search_enabled contributes up to limit objects to the same response.

It must be an integer between 1 and the configured ceiling. Anything else — a non-numeric value, zero, a negative number, or a number above the ceiling — is refused with 422 rather than silently corrected. Omitting limit (or sending limit= with nothing after it) falls back to the default. Both ends are configurable through the HABARICO_SEARCH setting:

# settings.py
HABARICO_SEARCH = {
"DEFAULT_LIMIT": 5,
"MAX_LIMIT": 100, # None removes the ceiling entirely
}
caution

The ceiling sits far below the page size of an admin's own list endpoint on purpose: the spotlight slices up to limit objects of every enabled admin and runs the match highlighter over each one. To read many rows of a single resource, page its list endpoint instead.

The rule lives in habarico_enterprise.search.utils.resolve_search_limit(), which AdminSearchView applies before the query fans out. site.get_search_result() takes the resolved integer, so a caller reaching it directly is responsible for the value it passes — run it through resolve_search_limit() to get the same validation the endpoint performs.

Match highlighting​

Installing habarico-enterprise also switches on match highlighting (meta.highlights) for every resource's own list search (?search=… on /admin/<resource>/) — independent of global_fulltext_search_enabled, which only affects the cross-admin endpoint. The free library still filters the list correctly on a plain install; it just never adds the highlights sidecar.

SearchHighlighter (habarico_enterprise.search.highlighter) is the class backing this. Override ModelAdmin.search_highlighter_class to customize it (snippet radius, mark tags, subclass with a different highlight()), or set it to None to opt a specific admin out of highlighting.

Diacritics-insensitive matching​

ADMIN_SEARCH_UNACCENT_ENABLED (see Fulltext search) also governs the highlighter: with it on, SearchHighlighter strips accents when locating matches and maps the offsets back onto the original, accented value.

SearchHighlighter takes an unaccent argument of its own that overrides the setting per instance; leaving it at None follows the setting.

Highlight sidecar in responses​

When a search is active, every matched row in both the global search endpoint (/admin/search/) and the resource list endpoint (/admin/<resource>/?search=… or /admin/<resource>/?filter=search="…") includes a highlights payload with the same shape:

[
{
"name": "name",
"value": "<mark>Alpha</mark> product",
"label": "Title",
"offsets": [[0, 5]]
}
]
  • name: the search_fields path that matched.
  • value: HTML-escaped rendition of the resolved field value with each matched substring wrapped in <mark>...</mark>. The global search endpoint trims to a snippet around the matches with leading/trailing …; the resource list endpoint returns the full value.
  • label: the field's verbose_name.
  • offsets: [start, end] half-open spans in the ORIGINAL (raw, non-escaped, non-marked) value. Independent of any snippet trimming applied to value.

In the global search endpoint the sidecar lives on each objects[i].highlights; in the resource list it lives on each row's meta.highlights.

See also​

  • Fulltext search — search_fields and per-resource ?search= filtering (free, unaffected by this page)