Django 配置搜索引擎 haystack 与 搜索页面无法返回数据问题
1、Django安装 haystack + whoosh + jieba
haystack是django的开源搜索框架,该框架支持Solr,Elasticsearch,Whoosh, 搜索引擎量。 Whoosh是一个搜索引擎使用,这是一个由纯Python实现的全文搜索引擎,没有二进制文件等,比较小巧,配置比较简单,性能略低。 Jieba是由Whoosh自带的是英文分词,对中文的分词支持不是太好,故用jieba替换whoosh的分词组件。
1pip install django-haystack 2pip install whoosh 3pip install jieba

2、创建项目app,配置settings.py文件
1# 全文检索框架haystack的配置 2HAYSTACK_CONNECTIONS = { 3 "default": { 4 # 指定使用的搜索引擎 5 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 6 # 'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine', 7 # 指定索引文件存放位置 8 'PATH': os.path.join(BASE_DIR, 'whoosh_index'), 9 'INCLUDE_SPELLING': True, 10 } 11} 12# 当添加、删除、修改数据时,自动生成索引 13HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor" 14# 每页显示条数 15HAYSTACK_SEARCH_RESULTS_PER_PAGE = 6

3、创建索引
在项目目录下创建search_indexes.py文件,文件名不能修改。
1from haystack import indexes 2# 对应模型 3from .models import Blog 4 5 6# 类名:模型名字 + Index 7class BlogIndex(indexes.SearchIndex, indexes.Indexable): 8 text = indexes.CharField(document=True, use_template=True) # 创建一个text字段 9 # blog_title = indexes.CharField(model_attr="blog_title") 10 # blog_content = indexes.CharField(model_attr="blog_content") 11 12 def get_model(self): 13 # 返回对应模型 14 return Blog 15 16 def index_queryset(self, using=None): 17 """Used when the entire index for model is updated.""" 18 return self.get_model().objects.all() 19

4、创建数据模板路径
blog/templates/search/indexes/blog/Blog_text.txt(模型名字 + _text.txt)
模板的作用是让text字段包含的内容,后面有搜索模板会用到。
txt中的内容根据模型类中需要进行搜索的字段进行设置。如我的models.py下的:
博客文章的标题和内容(blog_title、blog_content)就是我搜索的字段。

5、配置路由
1from django.contrib import admin 2from django.urls import path, include 3from blog.views import * 4 5urlpatterns = [ 6 # path('admin/', admin.site.urls), 7 path('blog_index/', blog_index, name="blog_index"), 8 path('search/', include('haystack.urls')), 9]

6、创建search.html
在目录"templates/search/"下建立search.html作为检索结果返回的页面(可自己进行定制)
1{% extends 'Conventional_Template.html' %} 2{% load static %} 3 4{% block title %} 5 <title>搜索页面</title> 6{% endblock %} 7 8{% block section %} 9 10 <section id="bricks"> 11 <div class="row masonry"> 12 <div style="display: flex;justify-content: center;align-items: center;"> 13 <h1>含关键字 -{{ query }}- 的博客文章</h1> 14 </div> 15 <!-- brick-wrapper --> 16 <div class="bricks-wrapper"> 17 <div class="grid-sizer"></div> 18 {% if query %} 19 {% for result in page.object_list %} 20 <article class="brick entry animate-this"> 21 <div class="entry-thumb"> 22 <a href="#" class="thumb-link"> 23 <img src="{% static 'images/thumbs/liberty.jpg' %}" alt="Liberty"> 24 </a> 25 </div> 26 <div class="entry-text"> 27 <div class="entry-header"> 28 <div class="entry-meta"> 29 <span class="cat-links"> 30 <a>标签</a> 31 </span> 32 <span class="cat-links"> 33 <a>标签</a> 34 </span> 35 </div> 36 <h1 class="entry-title"><a href="#">{{ result.object.blog_title }}</a></h1> 37 </div> 38 <div class="entry-excerpt"> 39 {% autoescape off %} 40 {{ result.object.blog_content|striptags|truncatechars:150 }} 41 {% endautoescape %} 42 </div> 43 </div> 44 </article> 45 {% endfor %} 46 {% endif %} 47 </div> <!-- end brick-wrapper --> 48 </div> <!-- end row --> 49 50 <!-- 分页功能 --> 51 <div class="row"> 52 <nav class="pagination"> 53 {% if page.has_previous %} 54 <span class="page-numbers prev"> 55 <a href="?q={{ query }}&page={{ page.previous_page_number }}">Prev</a> 56 </span> 57 {% else %} 58 {% endif %} 59 60 {% for pindex in paginator.page_range %} 61 {% if pindex == page.number %} 62 <a href="?q={{ query }}&page={{ pindex }}" class="page-numbers">{{ pindex }}</a> 63 {% else %} 64 <a href="?q={{ query }}&page={{ pindex }}" class="page-numbers">{{ pindex }}</a> 65 {% endif %} 66 {% endfor %} 67 68 {% if page.has_next %} 69 <a href="?q={{ query }}&page={{ page.next_page_number }}" class="page-numbers next">Next</a> 70 {% else %} 71 {% endif %} 72 </nav> 73 </div> 74 </section> 75 76{% endblock %}
要注意修改路由路径,name="q"是固定的。

7、创建ChineseAnalyzer.py文件
chenzhanxu_blog\Lib\site-packages\haystack\backends (虚拟环境中-找到haystack下的backends 然后创建ChineseAnalyzer.py文件)
1import jieba 2from whoosh.analysis import Tokenizer, Token 3 4 5class ChineseTokenizer(Tokenizer): 6 def __call__(self, value, positions=False, chars=False, 7 keeporiginal=False, removestops=True, 8 start_pos=0, start_char=0, mode='', **kwargs): 9 t = Token(positions, chars, removestops=removestops, mode=mode, 10 **kwargs) 11 seglist = jieba.cut(value, cut_all=True) 12 for w in seglist: 13 t.original = t.text = w 14 t.boost = 1.0 15 if positions: 16 t.pos = start_pos + value.find(w) 17 if chars: 18 t.startchar = start_char + value.find(w) 19 t.endchar = start_char + value.find(w) + len(w) 20 yield t 21 22 23def ChineseAnalyzer(): 24 return ChineseTokenizer()
8、复制whoosh_backend.py文件,改名为whoosh_cn_backend.py
chenzhanxu_blog\Lib\site-packages\haystack\backends (虚拟环境中-找到haystack下的backends目录下的whoosh_backend.py,然后复制(复制的文件名末尾有个空格,记得删除)到当前目录下改名whoosh_cn_backend.py) 记得在whoosh_cn_backend.py文件中添加ChineseAnalyzer.py文件。
1from haystack.backends.ChineseAnalyzer 2import ChineseAnalyzer
然后查找whoosh_cn_backend.py文件中schema_fields[field_class.index_fieldname] = TEXT()。
1#schema_fields[field_class.index_fieldname] = TEXT( 2# stored=True, 3# analyzer=field_class.analyzer or StemmingAnalyzer(), 4# field_boost=field_class.boost, 5# sortable=True, 6# ) 7schema_fields[field_class.index_fieldname] = TEXT( 8 stored=True, analyzer=ChineseAnalyzer(), 9 field_boost=field_class.boost, 10 sortable=True, 11)

9、生成索引
1python manage.py rebuild_index 2# (可选更新索引)python manage.py update_index
如果在settings.py添加了HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'就不用手动更新索引,系统会自动更新。
生成成功后在根目录下会生成一个whoosh_index的文件夹。

配置 haystack 搜索引擎的问题
1、search.html搜索页面无法返回数据问题
问题原因:可能是PyCharm创建Django是定义templates为模板文件,导致 haystack 无法自动找到templates文件夹,也就无法找到templates文件夹下的search.html模板,无法渲染成功。
解决方法:PyCharm创建Django项目时会把templates文件夹定义成为模板文件,要改成普通文件夹。

成品效果图

