上一篇:搭建基于Django的博客系统数据库迁移从Sqlite3到MySQL(四)
下一篇:基于Django的博客系统之增加类别导航栏(六)
功能概述
- 添加搜索框用于搜索博客。
需求详细描述
1. 添加搜索框用于搜索博客
- 描述: 在博客首页添加搜索框,用户可以通过关键词搜索博客文章。
- 功能要求:
- 搜索框位置:导航栏或页面顶部。
- 支持根据标题和内容进行搜索。
- 搜索结果显示匹配的博客列表。
- 用户故事:
- 作为用户,我希望能够通过搜索框快速找到感兴趣的博客文章。
技术结构
实现一个博客搜索功能,可以同时使用 Elasticsearch 和 MySQL 各自的优势。以下是如何区分和结合使用这两种技术的方法:
使用 MySQL 的部分
- 数据存储:
- 存储博客文章的基本信息,如标题、作者、发布时间、分类等。
- 存储用户信息、评论、标签等结构化数据。
- 基础查询和管理:
- 进行常规的 CRUD 操作,如创建、读取、更新、删除博客文章。
- 进行简单的过滤和排序,如按发布时间、作者、分类等查询文章。
使用 Elasticsearch 的部分
- 全文搜索:
- 存储和索引博客文章的全文内容,以支持全文搜索功能。
- 对用户搜索的关键词进行快速匹配,返回相关的文章列表。
- 复杂查询:
- 支持复杂的查询需求,如模糊搜索、布尔搜索、按相关性排序等。
- 支持自动补全、拼写纠错等高级搜索功能。
集成 MySQL 和 Elasticsearch
- 数据同步:
- 需要将 MySQL 中的博客文章数据同步到 Elasticsearch 中,以确保搜索功能能够使用最新的数据。
- 可以使用定时任务或实时数据同步机制来保持两者数据的一致性。例如,使用工具如 Logstash、Beats 或自定义的同步程序。
- 搜索接口设计:
- 提供一个统一的搜索接口,前端用户提交搜索请求时,后台从 Elasticsearch 中查询搜索结果。
- 查询到的搜索结果中包含文章的基本信息,从 Elasticsearch 返回文章的 ID,然后从 MySQL 中获取详细信息(如作者、评论等)。
实现步骤
要在 Django 博客应用中添加搜索框,以便用户可以搜索博客内容,可以按照以下步骤进行:
1. 安装 Django 搜索库
首先,确保你已经安装了 Django 搜索库。一个常用的选择是 django-haystack
库,它提供了与多个搜索引擎(如 Elasticsearch、Solr 和 Whoosh 等)集成的功能。
pip install django-haystack
2. 配置搜索引擎
选择并配置你想要使用的搜索引擎。例如,如果选择使用 Whoosh 搜索引擎,需要在 settings.py
文件中配置 Haystack 设置:
# settings.py
INSTALLED_APPS = [
# 其他应用...
'haystack',
]
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://localhost:9200/', # Elasticsearch 服务器的 URL
'INDEX_NAME': 'haystack', # Elasticsearch 索引的名称
},
}
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
3. 创建搜索索引类
为你的博客模型创建一个搜索索引类,告诉 Haystack 如何索引你的数据。假设你有一个名为 Post
的博客模型:
# search_indexes.py
from haystack import indexes
from .models import Post
class PostIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Post
在这个示例中,我们使用 text
字段来索引博客内容。你可以根据需要添加更多的字段。
4. 创建搜索模板
在你的博客模板文件夹中创建一个模板文件,用于显示搜索结果。这个模板将根据搜索结果显示匹配的博客文章。
<!-- templates/search/post_text.txt -->
{{ object.title }}
{{ object.content }}
同步索引:运行 Django 的管理命令来创建或更新搜索索引。
python manage.py rebuild_index
5. 更新 URL 配置
将搜索视图添加到你的 URL 配置中,以便用户可以访问搜索页面并执行搜索。
# urls.py
from django.urls import path
from . import views
urlpatterns = [
# 其他 URL 配置...
path('search/', views.SearchView.as_view(), name='search'),
]
6. 创建搜索视图
创建一个视图类来处理搜索请求,并将搜索结果呈现给用户。
from haystack.query import SearchQuerySet
def search_view(request):
query = request.GET.get('q', '')
results = SearchQuerySet().filter(content=query)
return render(request, 'search_results.html', {'results': results})
7. 创建搜索模板
创建一个模板文件,用于显示搜索结果。在这个模板中,你可以根据需要定制搜索结果的展示方式。
<!-- templates/search.html -->
{% for result in page.object_list %}
<h3><a href="{{ result.object.get_absolute_url }}">{{ result.object.title }}</a></h3>
<p>{{ result.object.content }}</p>
{% empty %}
<p>No results found.</p>
{% endfor %}
{% if is_paginated %}
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
<a href="?q={{ query }}&page=1">« first</a>
<a href="?q={{ query }}&page={{ page.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
<a href="?q={{ query }}&page={{ page.next_page_number }}">next</a>
<a href="?q={{ query }}&page={{ page.paginator.num_pages }}">last »</a>
{% endif %}
</span>
</div>
{% endif %}
8. 创建搜索表单
最后,创建一个搜索表单,让用户输入搜索关键字并提交搜索请求。
<!-- templates/search_form.html -->
<form action="{% url 'search' %}" method="get">
<input type="text" name="q" placeholder="Search...">
<button type="submit">Search</button>
</form>
将搜索表单包含在你的博客页面中的适当位置,用户就可以使用它来搜索博客内容了。
这就是在 Django 博客应用中添加搜索框的基本步骤。根据你的需求,你可以进一步定制搜索功能,例如添加搜索结果的高亮显示、自定义搜索表单字段等。
连接elasticsearch
具体参看这篇Windows安装ElasticSearch版本7.17.0
要连接 Elasticsearch,你需要配置 Django Haystack 的后端为 Elasticsearch 后端。下面是一些步骤:
- 安装 Elasticsearch:首先确保你已经安装了 Elasticsearch 并且它正在运行。你可以从 Elasticsearch 的官方网站上下载并安装它。
- 安装 Haystack:确保你已经安装了 Django Haystack。你可以使用 pip 进行安装:
pip install django-haystack
。 - 安装 Elasticsearch 后端:你需要安装与 Elasticsearch 兼容的 Haystack 后端。通常情况下,你需要安装
elasticsearch-dsl
,它是 Elasticsearch 的 Python 客户端库。你可以使用 pip 进行安装:pip install elasticsearch-dsl
。 - 配置 Haystack 设置:在 Django 项目的
settings.py
文件中,配置 Haystack 设置以使用 Elasticsearch 后端。这包括指定 Elasticsearch 的主机和端口等信息。
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://localhost:9200/', # Elasticsearch 服务器的 URL
'INDEX_NAME': 'haystack', # Elasticsearch 索引的名称
},
}
- 建立索引:运行
python manage.py rebuild_index
命令来建立你的模型的搜索索引。这将会在 Elasticsearch 中创建对应的索引。 - 运行你的应用程序:现在你的 Django 应用程序应该能够连接到 Elasticsearch 并使用它来进行全文搜索了。
问题和解决
报错 from django.utils.datetime_safe import date, datetime ModuleNotFoundError: No module named 'django.utils.datetime_safe'
分析解决:
在 Django 3.2 中,django.utils.datetime_safe
模块已被移除。这个模块提供了在处理日期和时间时的安全操作,以防止由于 datetime
和 date
对象的类型不同而导致的一些问题。
如果你的代码中依赖了 django.utils.datetime_safe
模块,你可以尝试以下替代方法:
- 直接使用 datetime 和 date:在绝大多数情况下,直接使用内置的
datetime
和date
类应该没有问题。只要确保在处理日期和时间时,类型的转换和比较是正确的即可。 - 在需要的地方进行转换:如果你的代码在某些情况下需要确保
datetime
和date
对象的类型一致,你可以手动进行转换。比如,使用timezone.now()
获取当前时间,使用datetime.strptime()
将字符串转换为datetime
对象等。 - 更新依赖:如果你使用的是第三方库,可以尝试更新这些库的版本,看看是否有与 Django 3.2 兼容的版本。
确保你的代码不再依赖于 django.utils.datetime_safe
模块后,就可以解决这个错误了。切换为elasticsearch
连接。报错如下 raise MissingDependency( haystack.exceptions.MissingDependency: The 'elasticsearch5' backend requires the installation of 'elasticsearch>=5.0.0,<6.0.0'. Please refer to the documentation.
修改settings.py
中HAYSTACK_CONNECTIONS
连接的Elasticsearch
版本为8.x,如下:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch8_backend.Elasticsearch8SearchEngine',
'URL': 'http://localhost:9200/',
'INDEX_NAME': 'myblog',
},
}
报错ModuleNotFoundError: No module named 'haystack.backends.elasticsearch8_backend'
原因:Haystack
中没有提供一个专门用于Elasticsearch
8.x 的后端引擎。使用 haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine
就可以支持 Elasticsearch
7.x。
重新安装elasticsearch
版本号为7.17。见这篇文章
卸载不需要的驱动
pip uninstall elasticsearch
pip uninstall elasticsearch-dsl
安装需要的驱动
pip install elasticsearch==7.17.0
pip install elasticsearch-dsl==7.4.1
再次调用启动搜索index
命令python manage.py rebuild_index
报错ImportError: cannot import name 'datetime_safe' from 'django.utils'
原因:Django 3.2 中移除了 django.utils.datetime_safe
模块引起的。Haystack 应该已经更新以适应 Django 3.2。
执行命令下载haystack支持django5.0.7
pip install git+https://github.com/django-haystack/django-haystack.git
再次调用启动搜索index
命令
python manage.py rebuild_index
运行成功。效果如下:
调用http://localhost:8000/search/?q=%E6%AF%94%E4%BC%AF
后,结果如下:
分析原因elasticsearch
的index有问题。
用tree /F
命令获取全文件夹下的文件格式表。
通过下面命令获取当前elasticsearch
里面的index
:
Invoke-WebRequest -Uri "http://localhost:9200/_cat/indices?v"
在post_text.txt
里面添加一段index
的指向
<!-- templates/search/indexes/blog/post_text.txt -->
执行index命令python manage.py rebuild_index
执行成功,如下:
postman
调用apihttp://localhost:9200/myblog
结果返回成功:
{
"myblog": {
"aliases": {},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "snowball"
},
"django_ct": {
"type": "keyword"
},
"django_id": {
"type": "keyword"
},
"id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"text": {
"type": "text",
"analyzer": "snowball"
},
"title": {
"type": "text",
"analyzer": "snowball"
}
}
},
"settings": {
"index": {
"max_ngram_diff": "2",
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "myblog",
"creation_date": "1716964128114",
"analysis": {
"filter": {
"haystack_ngram": {
"type": "ngram",
"min_gram": "3",
"max_gram": "4"
},
"haystack_edgengram": {
"type": "edge_ngram",
"min_gram": "2",
"max_gram": "15"
}
},
"analyzer": {
"edgengram_analyzer": {
"filter": [
"haystack_edgengram",
"lowercase"
],
"tokenizer": "standard"
},
"ngram_analyzer": {
"filter": [
"haystack_ngram",
"lowercase"
],
"tokenizer": "standard"
}
}
},
"number_of_replicas": "1",
"uuid": "1GzZNjZzSmOtmFdLuGWhvw",
"version": {
"created": "7170099"
}
}
}
}
}
搜索index
修改搜索视图以获取匹配的博客文章 ID,并从 MySQL 中获取详细信息:
# blog/views.py
from django.shortcuts import render
from haystack.query import SearchQuerySet
from .models import BlogPost
from .forms import BlogSearchForm
def search(request):
form = BlogSearchForm(request.GET)
results = []
if form.is_valid():
# 从 Elasticsearch 获取匹配的博客文章 ID
sqs = form.search()
matched_ids = [result.pk for result in sqs]
# 从 MySQL 数据库中获取详细信息
results = BlogPost.objects.filter(id__in=matched_ids)
return render(request, 'search_results.html', {'form': form, 'results': results})
访问’http://localhost:8000’输入搜索growing,访问’http://localhost:8000/search/?q=growing’,得到结果如下: