Skip to content

Django实现全文搜索

858字约3分钟

PythonDjango

2025-06-03

0.依赖安装

这里使用 django-haystack 来实现全文搜索的能力,django-haystack 是 Django 的全文搜索框架,能够将搜索引擎(如 WhooshElasticsearchSolr)集成到 Django 项目中,提供统一的搜索 API,支持多种后端搜索引擎。

pip install django-haystack whoosh

安装如上依赖后,需要在 settings.py 中配置如下参数:

INSTALLED_APPS = [
    # ...
    'haystack',
]

# 配置 Haystack 使用 Whoosh 作为搜索后端
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),  # 索引存储路径
    },
}

# 实时信号处理器 —— 每次模型变化自动更新索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

1.创建索引类

在需要索引的应用下方,创建名为 search_indexes.py文件,这个名称不能够修改。

例如要进行检索的模型对象如下:

class ArticlePost(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=100, unique=True)
    content = models.TextField()
    created = models.DateTimeField(default=timezone.now)
    updated = models.DateTimeField(auto_now=True)
    is_deleted = models.BooleanField(default=False)

则创建的索引类如下所示:

from haystack import indexes
from article.models import ArticlePost

class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
    # text是提供搜索的字段
    text = indexes.CharField(document=True, use_template=True)
    # 这里要索引的字段依照需求添加,不用全部添加
    author = indexes.CharField(model_attr='author')
    title = indexes.CharField(model_attr='title')
    content = indexes.CharField(model_attr='body')
    uuid = indexes.CharField(model_attr='uuid')
    created = indexes.DateTimeField(model_attr='created')

    def get_model(self):
        return ArticlePost

    def index_queryset(self, using=None):
        return self.get_model().objects.all()

2.全文搜索主字段模板

创建如下模版文件:

templates/search/indexes/[app名称]/[索引的原始对象名称]_text.txt
# 放到这里就是
templates/search/indexes/article/articlepost_text.txt

模板的内容如下,用于合并多个字段到 text 字段(全文搜索主字段)

{{ object.title }}
{{ object.content }}

3.生成索引字段

python manage.py rebuild_index

如果更新了数据或模型字段,可以重新运行这个命令。

image-20250603001528556

4.创建搜索路由和模板文件

urls.py 中添加搜索视图的路由,如下所示

from django.urls import path, include

urlpatterns = [
    # ...
    path('search/', include('haystack.urls')),  # 会自动生成 /search/?q=xxx 的搜索页面
]

编写搜索视图的模板文件 templates/search/search.html,这里也可以直接使用,但是原始的搜索视图通常不可用,这里自定义编写搜索视图的模板页面:

<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
{% load article_extras %}

<!-- 写入 base.html 中定义的 title -->
{% block title %}
搜索 | {{SITE_NAME}}
{% endblock title %}

<!-- 写入 base.html 中定义的 content -->
{% block content %}

<!-- 定义放置文章标题的div容器 -->
<div class="container search-container">
    <div class="card mt-5 col-8">
        <div class="card-line">
            <strong class="mt-3 ms-3 left">检索字段:{{query}}</strong>
            <p class="mt-3 me-3 right">共 {{ page.object_list|length }} 条结果</p>
        </div>
        <div class="border-bottom border-1 my-2"></div>
        <div class="mt-2 ms-3 mb-2">站内搜索是对网站全文内容进行检索,将整个网站的文章的标题和内容进行分词存储,然后给每个分词建立索引,搜索功能返回的是全站文章中标题或者内容中包含关键词的文章。</div>
    </div>
    <div class="mt-3 col-8">
        {% for article in page.object_list %}
        <div class="card search-card card-shadow list-group-item mt-3">
            <a href="/article/detail/{{ article.uuid }}/" class="text-decoration-none">
                <div class="card-body">
                    <h5 class="card-title text-dark">
                        {{article.title}}
                    </h5>
                    <p class="mb-1 text-muted small">{{ article.body|truncatechars:150 }}</p>
                </div>
            </a>
        </div>
        {% empty %}
        <div class="list-group-item text-center text-muted">
            <p class="my-3">没有找到匹配的文章</p>
        </div>
        {% endfor %}
    </div>
</div>

{% endblock %}

最终实现的效果如下所示:

搜索