什么是django-haystack?
官网的介绍如下
Haystack provides modular search for Django,It features a unified, familiar API that allows you to plug in different search backends (such as Solr, Elasticsearch, Whoosh, Xapian, etc.) without having to modify your code.
翻译过来就是
haystack提供了模块化的Django搜索框架,它具有一个统一的,熟悉的API可让您在不同的搜索后端(如Solr,Elasticsearch,Whoosh,Xapian等等)插件,而无需修改代码。
安装依赖库以及更换分词器
pip install whoosh django-haystack jieba
依次用到的是whoosh搜索引擎,django-haystack搜索框架,jieba分词之所以需要jieba库是因为Whoosh自带的是英文分词,对中文的分词支持不是太好。具体修改方式为拷贝/haystack/backends/whoosh_backend.py
放到/blog/blog/
目录下,更改文件名为whoosh_cn_backend.py
需要修改的部分为
...
from jieba.analyse import ChineseAnalyzer
...
else:
schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True)
在settings中添加Haystack到Django的INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'haystack',
'article',
'video',
]
文章应用模型搜索
建立模型
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200,verbose_name="文章标题")
summary=models.CharField(max_length=500,verbose_name="文章摘要")
content = models.TextField(verbose_name="文章内容")
views = models.IntegerField(verbose_name='浏览数', default=0)
def __str__(self):
return self.title
创建文章内容检索模板
创建之前先看下haystack的源码
def prepare_template(self, obj):
"""
Flattens an object for indexing.
This loads a template
(``search/indexes/{app_label}/{model_name}_{field_name}.txt``) and
returns the result of rendering that template. ``object`` will be in
its context.
"""
if self.instance_name is None and self.template_name is None:
raise SearchFieldError("This field requires either its instance_name variable to be populated or an explicit template_name in order to load the correct template.")
if self.template_name is not None:
template_names = self.template_name
if not isinstance(template_names, (list, tuple)):
template_names = [template_names]
else:
app_label, model_name = get_model_ct_tuple(obj)
template_names = ['search/indexes/%s/%s_%s.txt' % (app_label, model_name, self.instance_name)]
t = loader.select_template(template_names)
return t.render({'object': obj})
从上述源码可以看出索引模板文件的路径格式是项目的模板目录下search/indexes/{应用名}/{模型名}_{变量名}.txt
,其中变量名默认就是索引字段也就是text索引字段,故文章的索引内容模板路径即是这样search/indexes/article/article_text.txt
同时根据源码也知道如果不按照约定的路径来,那么就必须要在在应用下的
search_indexes.py里面的模型索引例如如ArticleIndex的
text = indexes.CharField(document=True, use_template=True,template_name="search/indexes/article/article_text.txt")
指定索引模板路径,我个人推荐指定template_name,便于后面的多模型搜索。
项目根目录下创建
templates/search/indexes/article/article_text.txt
文件内容如下:
{{ object.title }} {{ object.summary }} {{ object.content }}
创建索引
新建
local_apps/article/search_indexes.py
文件
如果想对应用下的模型建立搜索,则需要在该应用下面建立search_indexes
.py文件,且文件名不能修改。内容如下:from haystack import indexes from local_apps.article.models import Article class ArticleIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True,template_name="search/indexes/article/article_text.txt") views=indexes.IntegerField(model_attr='views') title=indexes.CharField(model_attr='title') def get_model(self): return Article def index_queryset(self, using='article_search'): return self.get_model().objects.all()
每个索引里面必须有且只能有一个字段为document=True,这代表haystack
和搜索引擎将使用此字段的内容作为索引进行检索。其他的字段并不作为检索数据
但是如果自定义了SearchView,重写了一些逻辑例如过滤,排序时用到了views,
则必须要加上views=indexes.IntegerField(model_attr=’views’)方便视图调用,不然会报错,如果没用到则可以不用加上。另外在index_queryset函数里的using默认为None,这里指定具体的搜索引擎,也是避免搜索视图里面忘记指定。
自定义文章搜索视图
from haystack.generic_views import SearchView
# 自定义文章搜索视图
class ArticleSearchView(SearchView):
# 默认搜索表单字段名
# search_field = 'q'
# 返回搜索结果集名称
context_object_name = 'articles'
# 设置分页
paginate_by = 10
# 搜索结果指定搜索连接同时以浏览量倒序
queryset = SearchQuerySet().using("article_search").order_by('-views')
# 视图模板
template_name = 'search/article_search_list.html'
如果不自定搜索表单,在没有覆盖默认的搜索字段名时,前台的搜索字段需要注意字段名默认为q,即如下<input name="q" type="text" value="" class="form-control>
建立路由
path(r'article/search/$', ArticleSearchView(), name='haystack_search'),
视频应用模型搜索
建立模型
class Video(models.Model):
name=models.CharField(verbose_name="视频名",max_length=200)
cover=models.URLField(verbose_name="视频封面",default="")
description=models.TextField(verbose_name="视频描述",default="")
playurl=models.TextField(verbose_name="视频播放url",default="")
viewnum=models.IntegerField(verbose_name="浏览数",default=0)
class Meta:
verbose_name="视频"
verbose_name_plural=verbose_name
def __str__(self):
return self.name
创建视频内容检索模板
项目根目录下创建
templates/search/indexes/video/video_text.txt
文件内容如下:
{{ object.name }} {{ object.description }}
创建索引
from haystack import indexes
from local_apps.video.models import Video
class VideoIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True,template_name="search/indexes/video/video_text.txt")
viewnum=indexes.IntegerField(model_attr="viewnum")
def get_model(self):
return Video
def index_queryset(self, using='video_search'):
# 过滤出允许显示以及所属类别允许显示的视频
return self.get_model().objects.all()
自定义文章搜索视图
from haystack.generic_views import SearchView
class VideoSearchView(SearchView):
# 返回搜索结果集
context_object_name = 'articles'
# 设置分页
paginate_by = 20
# 搜索结果以浏览量排序
queryset = SearchQuerySet().using("article_search").order_by('-views')
建立路由
path(r'video/search/', VideoSearchView.as_view(), name='video_search'),
配置多模型搜索
由于多模型搜索的特殊性,单模型搜索已经不满足默认的,同时还要支持当文章或者视频更新需要同步更新索引,需要增加一些配置在settings.py里面
HAYSTACK_CONNECTIONS = {
'default': {
# 选择语言解析器为自己更换的结巴分词
'ENGINE': 'blog.whoosh_cn_backend.WhooshEngine',
# 保存索引文件的地址,选择主目录下,这个会自动生成
'PATH': os.path.join(BASE_DIR,'default_whoosh_index'),
'EXCLUDED_INDEXES': ['apps.content.search_indexes.VideoIndex',
'apps.article.search_indexes.ArticleIndex'],
},
'article_search': {
# 选择语言解析器为自己更换的结巴分词
'ENGINE': 'blog.whoosh_cn_backend.WhooshEngine',
# 保存索引文件的地址,选择主目录下,这个会自动生成
'PATH': os.path.join(BASE_DIR,'article_whoosh_index'),
'STORAGE': 'file',
'POST_LIMIT': 128 * 1024 * 1024,
'INCLUDE_SPELLING': True,
'BATCH_SIZE': 100,
'EXCLUDED_INDEXES': ['apps.content.search_indexes.VideoIndex'],
},
'video_search': {
'ENGINE': 'apps.website.whoosh_cn_backend.WhooshEngine',
'PATH': os.path.join(BASE_DIR,'video_whoosh_index'),
'STORAGE': 'file',
'POST_LIMIT': 128 * 1024 * 1024,
'INCLUDE_SPELLING': True,
'BATCH_SIZE': 5000,
'EXCLUDED_INDEXES': ['apps.article.search_indexes.ArticleIndex'],
},
}
# 增加haystack连接路由
HAYSTACK_ROUTERS = ['haystack.routers.DefaultRouter','apps.article.routers.ArticleRouter','apps.video.routers.VideoRouter']
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'#默认按照最新时间更新索引
还需要在视频和文章应用的目录下新建routers.py内容分别为如下:
from haystack import routers
class ArticleRouter(routers.BaseRouter):
def for_write(self, **hints):
return 'article_search'
def for_read(self, **hints):
return 'article_search'
from haystack import routers
class VideoRouter(routers.BaseRouter):
def for_write(self, **hints):
return 'video_search'
def for_read(self, **hints):
return 'video_search'
重建索引
重建全部
python manage.py rebuild_index
因为上面的haystack默认连接已经去除了文章和视频的索引类,故执行完上述命令后,default_whoosh_index文件夹下不会生成索引信息的只重建文章
python manage.py rebuild_index -u article_search
只重建视频
python manage.py rebuild_index -u video_search