上一篇:搭建基于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.pyINSTALLED_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.pyfrom haystack import indexes
from .models import Postclass 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.pyfrom django.urls import path
from . import viewsurlpatterns = [# 其他 URL 配置...path('search/', views.SearchView.as_view(), name='search'),
]
6. 創建搜索視圖
創建一個視圖類來處理搜索請求,并將搜索結果呈現給用戶。
from haystack.query import SearchQuerySetdef 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.0pip 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 BlogSearchFormdef search(request):form = BlogSearchForm(request.GET)results = []if form.is_valid():# 從 Elasticsearch 獲取匹配的博客文章 IDsqs = 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’,得到結果如下: