• DJANGO-天天生鲜项目从0到1-009-搜索功能实现(django-haystack+whoosh+jieba)


    一般搜索功能架构为:搜索框架+搜索引擎(包括分词方式)

    这里搜索框架选择:

    django-haystack:是django的开源搜索框架,该框架支持Solr,Elasticsearch,Whoosh, *Xapian*搜索引擎,不用更改代码,直接切换引擎,减少代码量。

    搜索引擎使用:

    Whoosh:这是一个由纯Python实现的全文搜索引擎,没有二进制文件等,比较小巧,配置比较简单,性能相对低一些,不过用于小网站足矣。

    分词方式使用:

    Jieba:由于Whoosh自带的是英文分词,对中文的分词支持不是太好,故用jieba替换whoosh的分词组件。

    系统环境信息为:Centos7,Python 3.8.2,Django 3.0.3

    djang-haystack

    安装

    pip install django-haystack

    注意这里安装的是djang-haystack,而不是pip install haystack,我第一次安装时安装成了haystack,然后又再运行了 pip install django-haystack,这样启动项目后可能会导致冲突而报错"cannot import name 'connections' from 'haystack' ",解决方式就是,将haystack和django-haystack都卸载掉,然后重新安装django-haystack即可

     安装完djang-haystack之后,启动项目又报了另外一个错:"cannot import name 'six' from 'django.utils",原因是django 3.x 系列删除了six.

     

     解决方式是:

    1. 安装six

    pip install six

    2. 进入报错的虚拟环境目录

    cd /home/gong/.conda/envs/dailyfresh/lib/python3.8/site-packages/

    将该目录下的six.py文件拷贝一份到该目录下的django/utils目录下

    cp six.py django/utils

    3. 修改虚拟环境的haystack目录下的inputs.py文件

    vim /home/gong/.conda/envs/dailyfresh/lib/python3.8/site-packages/haystack/inputs.py
    # 
    # from django.utils.encoding import force_text, python_2_unicode_compatible
    # 改为
    from django.utils.encoding import force_text
    from django.utils.six import python_2_unicode_compatible

    配置haystack

    1. 修改setting.py

    添加haystack应用

    INSTALLED_APPS = [
        ...
        'haystack',# 全文搜索框架
        ...       
    ]

    配置搜索引擎和索引文件

    import os
    HAYSTACK_CONNECTIONS = {
        'default': {
            'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
            'PATH': os.path.join(BASE_DIR), 'whoosh_index'),
        },
    }

    'ENGINE':搜索引擎使用的是虚拟环境中的haystack路径下的whoosh_backend.py文件中的WhooshEngine

    'PATH':指的是生成的索引文件所在路径,即项目根目录下的whoosh_index文件夹路径,这个路径不需要手动创建,当运行生成索引文件命令时会自动创建

    设置搜索结果页面每页显示的数量,默认为20个

    HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10

    2. 索引文件的生成

    注意下述路径和文件名都是固定的,其中其他都可以不用动,替换一下model的名字即可

    在goods应用目录下新建一个search_indexes.py文件,在其中定义一个商品索引类。

    from haystack import indexes
    from goods.models import Goods
    
    class GoodsIndex(indexes.SearchIndex, indexes.Indexable):
        text = indexes.CharField(document=True, use_template=True)
    
        def get_model(self):
            return Goods
    
        def index_queryset(self, using=None):
            """Used when the entire index for model is updated."""
            return self.get_model().objects.all()

    在templates下面创建文件templates/search/indexes/goods/good_text.txt

    {{ object.name }}
    {{ object.brief }}
    {{ object.goodsspu.detail }}

    生成索引文件:

    python manage.py rebuild_index

    3. 配置查询框和url

    编辑查询框,注意method必须为get,action可以自定义表单提交路径,输入框的name必须为"q"

    <div class="search_con fl">
        <form method="get" action="{% url 'goods:search' %}">
            <input type="text" class="input_text fl" name="q" placeholder="搜索商品">
            <input type="submit" class="input_btn fr" name="" value="搜索">
        </form>
    </div>

    编辑url,根据上面的action地址编辑对应的url,这里是编辑goods应用下的urls.py。

    from django.urls import path
    from .views import GoodsSearchView
    
    urlpatterns = [
        ...
        # path('search/', include('haystack.urls')),
        path('search/', GoodsSearchView.as_view(), name='search'),
        ...
    ]

    注意这里有两种写法:

    第一种为include('haystack.urls'),不自定义处理视图,直接使用haystack默认的context返回给模板文件,content内容为:

    context = {
                'query': self.query, # 搜索关键字
                'form': self.form,
                'page': page, # 当前页的page对象,遍历page对象,获取到的是SearchResult类的实例对象,对象的属性object才是模型类的对象。
                'paginator': paginator, # 分页paginator对象
                'suggestion': None,
            }

    第二种为自定义处理视图,GoodsSearchView.as_view(),可以新增自定义需要的数据信息给模板文件

    class GoodsSearchView(SearchView):
        '''商品搜索视图'''
        def get_context_data(self, *args, **kwargs):
            # 继承获取预定义的内容
            context = super(GoodsSearchView, self).get_context_data(*args, **kwargs)
            # 获取想要的数据库信息
            # 获取全部商品种类
            all_type = GoodsType.objects.all()
            # 获取购物车数量
            cart_count = get_cart_count(self.request)
            # 添加上下文
            context['all_type'] = all_type
            context['cart_count'] = cart_count
            context['page'] = context['page_obj']
            print(context)
            return context

    这里注意新增了一个key为‘page’的属性,其值为预定义的key为‘page_obj’的值,原因是当我们使用上述第一种默认返回值时,page对象的key为‘page’,而第二种自定义视图中page对象的key为‘page_obj’

    为了保持一致且一般情况我们都使用的key为‘page’代表page对象。

    启动项目进行查询,发现页面报错:TemplateDoesNotExist,原因是haystack将查询结果展示在templates/search/search.html中,因此需要创建并编辑该文件

    4. 编辑search.html

    {{ query }}为查询条件,{{ page }}和{{ paginator }}对象与django的分页器对象一致,所有的属性和方法也一样。

    注意这里的分页页码指向的地址,需要拼上?q={{查询关键字}}&amp;page={{页码}}

    {% extends 'base_list.html'%}
    {% block title %}天天生鲜-搜索结果{% endblock title%}
    {% block detail%}
    <div class="breadcrumb">
        <a href="#">{{ query }}</a>
        <span>></span>
        <a href="#">搜索结果</a>
    </div>
    搜索字段:{{ query }}</br>
    page对象:{{ page }}</br>
    paginator对象:{{ paginator }}</br>
    <div class="main_wrap clearfix">
        <ul class="goods_type_list clearfix">
            {% for result in page %}
            <li>
                result
                <a href="{% url 'goods:detail' result.object.id %}"><img src="{{ result.object.image.url }}"></a>
                <h4><a href="{% url 'goods:detail' result.object.id %}">{{ result.object.name }}</a></h4>
                <div class="operate">
                    <span class="prize">¥{{ result.object.price }}</span>
                    <span class="unit">{{ result.object.price }}/{{ result.object.uom }}</span>
                    <a href="#" class="add_goods" title="加入购物车"></a>
                </div>
            </li>
            {% empty %}
            未搜索到结果
            {% endfor %}
        </ul>
    
        <div class="pagenation">
            {% if page.has_previous %}
            <a href="{% url 'goods:search' %}?q={{ query }}&amp;page={{ page.previous_page_number }}"><上一页</a>
            {% endif %}
            {% for num in paginator.page_range %}
            <a href="{% url 'goods:search' %}?q={{ query }}&amp;page={{ num }}" {% if num == page.number %}class="active"{% endif %}>{{ num }}</a>
            {% endfor %}
            {% if page.has_next %}
            <a href="{% url 'goods:search' %}?q={{ query }}&amp;page={{ page.next_page_number }}">下一页></a>
            {% endif %}
        </div>
    </div>
    {% endblock detail %}

     jieba分词模块

    由于原whoosh自带的分词方式对中文支持不是很好,于是使用对中文分词更好的jieba模块

    安装jieba

    pip install jieba

    修改引擎文件

    进入虚拟环境下的haystack/backends目录

    cd /home/gong/.conda/envs/dailyfresh/lib/python3.8/site-packages/haystack/backends/

    创建ChineseAnalyzer.py文件

    import jieba
    from whoosh.analysis import Tokenizer, Token
    
    class ChineseTokenizer(Tokenizer):
        def __call__(self, value, positions=False, chars=False,
                     keeporiginal=False, removestops=True,
                     start_pos=0, start_char=0, mode='', **kwargs):
            t = Token(positions, chars, removestops=removestops, mode=mode, **kwargs)
            seglist = jieba.cut(value, cut_all=True)
            for w in seglist:
                t.original = t.text = w
                t.boost = 1.0
                if positions:
                    t.pos = start_pos + value.find(w)
                if chars:
                    t.startchar = start_char + value.find(w)
                    t.endchar = start_char + value.find(w) + len(w)
                yield t
    
    def ChineseAnalyzer():
        return ChineseTokenizer()

    新建复制whoosh_backend.py,命名为whoosh_cn_backend.py,编辑whoosh_cn_backend.py,引入中文分析类,内部采用jieba分词

    cp whoosh_backend.py whoosh_cn_backend.py
    from .ChineseAnalyzer import ChineseAnalyzer
    
    ...
    # 查找
    # analyzer=StemmingAnalyzer()
    # 改为
    analyzer=ChineseAnalyzer()

    修改settings.py文件

    将原whoosh_backend改成whoosh_cn_backend

    # haystack+whoosh配置,全文检索框架
    HAYSTACK_CONNECTIONS = {
        'default': {
            #  'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
            'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
            'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
        },
    }
    # 设置搜索结果每页显示多少条数据
    HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10

    重新创建索引数据

    python manage.py rebuild_index
  • 相关阅读:
    Mysql卸载
    Mysql安装
    Mysql升级、免安装版MYSQL安装与卸载
    Mysql导入csv文件
    Mysql导入sql文件
    日期月份是英文转成数字——oracle
    两表关联更新数据——oracle
    oracle获取排序第一的数据
    case...when...和decode——oracle
    oracle问题:char类型数据查询不到
  • 原文地址:https://www.cnblogs.com/gcxblogs/p/12822168.html
Copyright © 2020-2023  润新知