Django Model Search
django-modelsearch is a Python library that enables indexing Django models with various search backends, including Elasticsearch, OpenSearch, and the database itself (PostgreSQL, MySQL). It provides a unified API for searching models, inspired by Wagtail Search. The current version is 1.2.0, with releases occurring periodically to support Django and backend updates.
Warnings
- breaking Support for OpenSearch 1.x was dropped in version 1.1, which aligns with OpenSearch's End-of-Life for that series. Users on OpenSearch 1.x must upgrade their OpenSearch cluster or use an older version of django-modelsearch (pre-1.1).
- breaking Version 1.0 was a fork from Wagtail's internal search module. If you are migrating from an older Wagtail project that used its built-in search before this fork, direct migration might require adjustments to settings and index definitions, as some internal paths or configurations may have changed.
- deprecated The hidden setting `_WAGTAILSEARCH_FORCE_AUTO_UPDATE` was removed in version 1.1.1. While not a public API, any reliance on this internal setting will cease to function.
- gotcha For PostgreSQL database search and certain ORM features (like `SearchVector`), you must include `django.contrib.postgres` in your `INSTALLED_APPS`.
- gotcha Configuring search backends (`MODELSEARCH_BACKENDS`) correctly is crucial. Misconfigurations can lead to indexing failures or incorrect search results without immediate errors. Pay close attention to backend types, URLs, and index names.
Install
-
pip install django-modelsearch -
pip install "django-modelsearch[elasticsearch]" -
pip install "django-modelsearch[opensearch]"
Imports
- Index
from modelsearch.base import Index
- SearchField
from modelsearch.fields import SearchField
- MatchAll
from modelsearch.query import MatchAll
- ElasticsearchSearchBackend
from modelsearch.backends.elasticsearch import ElasticsearchSearchBackend
Quickstart
import os
from django.db import models
from modelsearch.base import Index
from modelsearch.fields import SearchField
from modelsearch.query import MatchAll
from django.conf import settings
# Minimal Django settings setup for quickstart, typically in settings.py
if not settings.configured:
settings.configure(
INSTALLED_APPS=[
"modelsearch",
"django.contrib.postgres" # For PostgreSQL DB search features
],
MODELSEARCH_BACKENDS={
"default": {
"BACKEND": "modelsearch.backends.database.DatabaseSearchBackend"
# "BACKEND": "modelsearch.backends.elasticsearch.ElasticsearchSearchBackend",
# "URLS": [os.environ.get('ELASTICSEARCH_URL', 'http://localhost:9200')],
# "INDEX": "test_modelsearch",
}
},
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
}
},
SECRET_KEY="dummy-secret-key"
)
# Define a Django Model
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
class Search(Index):
fields = [
SearchField("title", boost=10),
SearchField("body"),
]
def __str__(self):
return self.title
# Example usage (run after Django setup and migrations)
# This part assumes a database is configured and tables are created.
# For a real application, you'd run `manage.py migrate` and then `manage.py update_index`.
if __name__ == "__main__":
# In a real Django project, you'd import Article and use its manager.
# For this isolated example, we simulate object creation.
# Note: Search index update requires running `manage.py update_index` or calling backend manually.
print("Quickstart will only demonstrate search query structure.")
print("To run fully, ensure Django is setup, migrated, and index updated.")
# Example search (using a dummy queryset for demonstration)
# In a real app, this would be: `Article.objects.search("search term")`
search_query = "example"
# Simulate a search call
class MockSearchManager:
def search(self, query):
print(f"Searching for: {query}")
return ["MockResult1", "MockResult2"]
mock_manager = MockSearchManager()
print(f"\nSearch for '{search_query}':")
results = mock_manager.search(search_query)
print(f"Results: {results}")
print("\nSearch for all with MatchAll():")
results_all = mock_manager.search(MatchAll())
print(f"Results: {results_all}")