A Django content management system with a user-friendly interface and powerful features for building websites and applications.
Full-text search capabilities with configurable backends, search field configuration, and powerful query interfaces. Wagtail provides comprehensive search functionality that integrates with multiple search engines and supports complex queries.
Classes for configuring which fields are searchable and how they're indexed.
class SearchField:
"""
Configures a field for full-text search indexing.
Parameters:
field_name (str): Name of the model field to index
partial_match (bool): Whether to enable partial matching
boost (int): Search result boost factor for this field
"""
def __init__(self, field_name, partial_match=True, boost=1):
"""Initialize search field configuration."""
class AutocompleteField:
"""
Configures a field for autocomplete search functionality.
Parameters:
field_name (str): Name of the model field for autocomplete
"""
def __init__(self, field_name):
"""Initialize autocomplete field configuration."""
class FilterField:
"""
Configures a field for exact match filtering in search results.
Parameters:
field_name (str): Name of the model field for filtering
"""
def __init__(self, field_name):
"""Initialize filter field configuration."""
class RelatedFields:
"""
Configures search through related model fields.
Parameters:
relation_name (str): Name of the relation to search through
fields (list): List of SearchField objects for related model
"""
def __init__(self, relation_name, fields):
"""Initialize related fields search configuration."""Base classes and mixins for making models searchable.
class Indexed:
"""
Mixin class that makes Django models searchable.
Add this mixin to any model to enable search functionality.
"""
search_fields: list # List of SearchField, AutocompleteField, FilterField objects
@classmethod
def search(cls, query, backend=None):
"""
Perform a search query on this model.
Parameters:
query (str): Search query string
backend (str): Search backend to use (optional)
Returns:
SearchResults: Query results with ranking and filtering
"""
def get_search_backend(backend_name=None):
"""
Get a search backend instance.
Parameters:
backend_name (str): Name of backend ('default', 'elasticsearch', etc.)
Returns:
SearchBackend: Configured search backend instance
"""Available search backend implementations with different capabilities.
class SearchBackend:
"""
Base class for search backends.
All search backends inherit from this class.
"""
def search(self, query, model_or_queryset, fields=None, filters=None, order_by_relevance=True):
"""
Execute a search query.
Parameters:
query (str): Search query string
model_or_queryset: Model class or QuerySet to search
fields (list): Fields to search in
filters (dict): Additional filters to apply
order_by_relevance (bool): Whether to order by search relevance
Returns:
SearchResults: Search results with metadata
"""
def autocomplete(self, query, model_or_queryset, fields=None):
"""
Get autocomplete suggestions.
Parameters:
query (str): Partial query for autocomplete
model_or_queryset: Model class or QuerySet for suggestions
fields (list): Fields to use for autocomplete
Returns:
list: List of autocomplete suggestions
"""
class DatabaseSearchBackend(SearchBackend):
"""
Search backend using Django's database with PostgreSQL full-text search.
Default backend that works with any Django-supported database.
"""
class ElasticsearchSearchBackend(SearchBackend):
"""
Search backend using Elasticsearch for advanced search capabilities.
Provides better performance and advanced features for large datasets.
"""Classes for handling and manipulating search results.
class SearchResults:
"""
Container for search results with metadata and filtering capabilities.
"""
def __init__(self, model_or_queryset, query, backend):
"""Initialize search results container."""
def __iter__(self):
"""Iterate over search results."""
def __len__(self):
"""Get number of search results."""
def __getitem__(self, index):
"""Get result at specific index or slice."""
def filter(self, **kwargs):
"""Apply additional filters to search results."""
def order_by(self, *fields):
"""Change the ordering of search results."""
def annotate_score(self, field_name='_score'):
"""Add search relevance score to results."""
class SearchQuery:
"""
Represents a search query with parsing and normalization.
"""
def __init__(self, query_string, operator='and', boost=None):
"""
Initialize search query.
Parameters:
query_string (str): Raw query string
operator (str): Query operator ('and' or 'or')
boost (dict): Field boost configuration
"""
def get_terms(self):
"""Get individual search terms from query."""
def get_filters(self):
"""Get filter conditions from query."""Utility functions and classes for search functionality.
def parse_query_string(query_string, operator='and'):
"""
Parse a query string into structured query components.
Parameters:
query_string (str): Raw search query
operator (str): Default operator between terms
Returns:
SearchQuery: Parsed query object
"""
class SearchPromotion:
"""
Model for promoting specific content in search results.
Allows administrators to boost certain pages for specific queries.
"""
query: str
page: Page
sort_order: int
description: str
@classmethod
def get_for_query(cls, query_string):
"""Get promotions for a specific query."""
def rebuild_search_index():
"""
Rebuild the entire search index for all searchable models.
Use this management command function to refresh search data.
"""
def update_search_index(model=None):
"""
Update search index for specific model or all models.
Parameters:
model (Model): Specific model to update (None for all models)
"""from wagtail.models import Page
from wagtail.search import index
from django.db import models
class BlogPage(Page):
"""Blog page with comprehensive search configuration."""
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = models.TextField()
tags = models.CharField(max_length=255, blank=True)
# Configure search fields
search_fields = Page.search_fields + [
index.SearchField('intro', partial_match=True, boost=2),
index.SearchField('body'),
index.SearchField('tags', partial_match=True),
index.AutocompleteField('title'),
index.FilterField('date'),
index.FilterField('live'),
]
class Author(models.Model):
"""Author model with search fields."""
name = models.CharField(max_length=100)
bio = models.TextField()
email = models.EmailField()
# Make non-page models searchable
search_fields = [
index.SearchField('name', partial_match=True, boost=3),
index.SearchField('bio'),
index.AutocompleteField('name'),
]
class Meta:
# Add Indexed mixin functionality
# In practice, inherit from index.Indexed
pass
# Add Indexed mixin to existing model
Author.__bases__ = (index.Indexed,) + Author.__bases__from wagtail.models import Page
from wagtail.search.models import Query
# Basic page search
search_results = Page.objects.live().search('django cms')
# Advanced search with filtering
blog_results = BlogPage.objects.live().search(
'web development'
).filter(
date__gte='2023-01-01'
).order_by('-date')
# Search with specific backend
from wagtail.search.backends import get_search_backend
elasticsearch_backend = get_search_backend('elasticsearch')
results = elasticsearch_backend.search(
'python tutorial',
BlogPage.objects.live(),
fields=['title', 'intro', 'body']
)
# Autocomplete search
suggestions = Page.objects.live().autocomplete('wagtai')
# Track search queries
Query.get('django cms').add_hit() # Record search analytics
popular_queries = Query.objects.filter(
hits__gte=10
).order_by('-hits')from django.shortcuts import render
from django.core.paginator import Paginator
from wagtail.models import Page
from wagtail.search.models import Query
def search_view(request):
"""Custom search view with pagination and analytics."""
query_string = request.GET.get('q', '')
page_number = request.GET.get('page', 1)
if query_string:
# Perform search
search_results = Page.objects.live().search(query_string)
# Track search query
query = Query.get(query_string)
query.add_hit()
# Add pagination
paginator = Paginator(search_results, 10)
results_page = paginator.get_page(page_number)
# Get search suggestions
suggestions = Page.objects.live().autocomplete(query_string)[:5]
else:
results_page = None
suggestions = []
return render(request, 'search/search.html', {
'query_string': query_string,
'results': results_page,
'suggestions': suggestions,
})
def faceted_search_view(request):
"""Search view with faceted filtering."""
query_string = request.GET.get('q', '')
content_type = request.GET.get('type', '')
date_filter = request.GET.get('date', '')
if query_string:
results = Page.objects.live().search(query_string)
# Apply facet filters
if content_type:
results = results.type(eval(content_type))
if date_filter:
results = results.filter(first_published_at__gte=date_filter)
# Get facet counts
facets = {
'types': results.facet('content_type'),
'dates': results.facet('first_published_at__year'),
}
else:
results = Page.objects.none()
facets = {}
return render(request, 'search/faceted_search.html', {
'query_string': query_string,
'results': results,
'facets': facets,
})from wagtail.models import Page
from wagtail.search import index
from django.db import models
from modelcluster.fields import ParentalKey
class BlogCategory(models.Model):
"""Blog category model."""
name = models.CharField(max_length=100)
description = models.TextField()
search_fields = [
index.SearchField('name', boost=2),
index.SearchField('description'),
]
class BlogPage(Page):
"""Blog page with category relationship."""
category = models.ForeignKey(
BlogCategory,
on_delete=models.SET_NULL,
null=True,
blank=True
)
body = models.TextField()
# Search through related fields
search_fields = Page.search_fields + [
index.SearchField('body'),
index.RelatedFields('category', [
index.SearchField('name', boost=2),
index.SearchField('description'),
]),
]
# Search will now find pages by category name/description
results = BlogPage.objects.search('python') # Finds pages in "Python" category# settings.py
WAGTAILSEARCH_BACKENDS = {
'default': {
'BACKEND': 'wagtail.search.backends.database',
'SEARCH_CONFIG': 'english', # PostgreSQL text search config
},
'elasticsearch': {
'BACKEND': 'wagtail.search.backends.elasticsearch7',
'URLS': ['http://localhost:9200'],
'INDEX': 'wagtail',
'TIMEOUT': 5,
'OPTIONS': {},
'INDEX_SETTINGS': {
'settings': {
'analysis': {
'analyzer': {
'ngram_analyzer': {
'tokenizer': 'ngram_tokenizer',
}
}
}
}
}
}
}
# Use specific backend
from wagtail.search.backends import get_search_backend
elasticsearch = get_search_backend('elasticsearch')
database = get_search_backend('default')
# Backend-specific search
es_results = elasticsearch.search('query', MyModel)
db_results = database.search('query', MyModel)# Management commands for search index
from django.core.management import call_command
# Rebuild entire search index
call_command('update_index')
# Update specific model
call_command('update_index', 'blog.BlogPage')
# Clear search index
call_command('clear_index')
# Programmatic index updates
from wagtail.search.signal_handlers import post_save_signal_handler
from wagtail.search import index
# Manual index update
page = BlogPage.objects.get(pk=1)
index.insert_or_update_object(page)
# Remove from index
index.remove_object(page)
# Bulk index operations
pages = BlogPage.objects.all()
for page in pages:
index.insert_or_update_object(page)Install with Tessl CLI
npx tessl i tessl/pypi-wagtail