商品的搜索
我们搜索的是商品信息,所以涉及goods层中的GoodsSKU表:
- 使用全文检索来进行搜索功能的实现,haystack 是 django的开源搜索框架,该框架支持 Solr, Elasticsearch, Whoosh, *Xapian*搜索引擎,不用更改代码,直接切换引擎,减少代码量。
- 搜索引擎使用 whoosh,这是一个由纯 Python 实现的全文搜索引擎,没有二进制文件等,比较小巧,配置比较简单,当然性能自然略低。
- 中文分词 Jieba,由于 Whoosh 自带的是英文分词,对中文的分词支持不是太好,故用 jieba 替换 whoosh 的分词组件。
首先也就是最重要的要提一下,有些的命名必须是严格规定好的,不允许改变,下面会重要的提一下:
首先就是安装django-haystack,jieba,whoosh,然后进入haystack安装包下面的backends下面,
创建ChineseAnalyzer.py文件,在文件中写入下面代码,保存,看下面代码就知道,通过jieba来处理汉字的分词,然后返回出去:
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分词。
from .ChineseAnalyzer import ChineseAnalyzer
更改词语分析类。
查找
analyzer=StemmingAnalyzer()
改为
analyzer=ChineseAnalyzer()
然后进入settings.py 配置haystack:
# 全文检索框架的配置
HAYSTACK_CONNECTIONS = {
'default': {
# 使用whoosh引擎
'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
# 索引文件路径
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
}
}
# 当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
首先进入我们的指定的应用下面创建一个文件:search_indexes.py,记住这个文件命名固定,同时写入以下内容,同时记住里面的类名称也是固定的:模型名称+Index, 函数名称都是固定的:
from haystack import indexes
from goods.models import GoodsSKU
#指定某些类建立索引
class GoodsSKUIndex(indexes.SearchIndex,indexes.Indexable):
#将索引文件创建好以后放在template下面的一个文件里面
text=indexes.CharField(document=True,use_template=True)
def get_model(self):
#返回指定模型
return GoodsSKU
#建立索引数据
def index_queryset(self, using=None):
#返回所有模型数据
return self.get_model().objects.all()
上面text中提到use_template,所以必须建立对应的模板文件,进入templates目录下,建立search/indexes/应用名称/商品表名称_text.txt,这些都是固定好的命名,同时在search目录下面建立search.html,如下截图效果:
Goodssku_text.txt 表示要按照什么来进行查询:
Search.html,表示点击搜索以后跳转的界面,对搜索商品进行展示,我们先看search_test.html,中的一些字段代表的名称,search.html是固定命名的,search_test.html是我们测试用的,这样命名,是无效的,只是为了我们测试用的,可以删了:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
{% load staticfiles %}
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>天天生鲜-商品搜索列表</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/reset.css'%}">
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
</head>
<body>
<div class="header_con">
<div class="header">
<div class="welcome fl">欢迎来到天天生鲜!</div>
<div class="fr">
{% if user.is_authenticated %}
<div class="login_info fl">
欢迎您:<em>{{ user.username }}</em>
<a href="{% url 'user:logout' %}">退出</a>
</div>
{% else %}
<div class="login_btn fl">
<a href="{% url 'user:login' %}">登录</a>
<span>|</span>
<a href="{% url 'user:register' %}">注册</a>
</div>
{% endif %}
<div class="user_link fl">
<span>|</span>
<a href="{% url 'user:user' %}">用户中心</a>
<span>|</span>
<a href="cart.html">我的购物车</a>
<span>|</span>
<a href="{% url 'user:order' %}">我的订单</a>
</div>
</div>
</div>
</div>
<div class="search_bar clearfix">
<a href="{% url 'goods:index' %}" class="logo fl"><img src="{% static 'images/logo.png' %}"></a>
<div class="search_con fl">
<form method="get" action="/search">
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>
</div>
<div class="guest_cart fr">
<a href="#" class="cart_name fl">我的购物车</a>
<div class="goods_count fl">{{ cart_count }}</div>
</div>
</div>
<div class="navbar_con">
<div class="navbar clearfix">
<div class="subnav_con fl">
<h1>全部商品分类</h1>
<span></span>
<ul class="subnav">
{% for type in types %}{# 显示所有分类 #}
<li><a href="{% url 'goods:list' type.id 1 %}" class="{{ type.logo }}">{{ type.name }}</a></li>
{% endfor %}
</ul>
</div>
<ul class="navlist fl">
<li><a href="{% url 'goods:index' %}">首页</a></li>
<li class="interval">|</li>
<li><a href="">手机生鲜</a></li>
<li class="interval">|</li>
<li><a href="">抽奖</a></li>
</ul>
</div>
</div>
<div class="breadcrumb">
<a href="#">{{ query }}</a>
<span>></span>
<a href="#">搜索结果如下:</a>{# 当前种类 #}
</div>
<div class="main_wrap clearfix">
<ul class="goods_type_list clearfix">
{% for item in page %}{# 每一页商品的详细信息 #}
<li>
<a href="{% url 'goods:detail' item.object.id %}"><img src="{{ item.object.image.url }}"></a>
<h4><a href="{% url 'goods:detail' item.object.id %}">{{ item.object.name }}</a></h4>
<div class="operate">
<span class="prize">¥{{ item.object.price }}</span>
<span class="unit">{{ item.object.price }}/{{ item.object.unite }}</span>
<a href="#" class="add_goods" title="加入购物车"></a>
</div>
</li>
{% endfor %}
</ul>
<div class="pagenation">{# 判断下一页显示 #}
{% if page.has_previous %}
<a href="/search?q={{ query }}&page={{ page.previous_page_number }}">上一页</a>
{% endif %}
{% for sku_page in paginator.page_range %}
{% if sku_page == skus_page.number %}
<a href="/search?q={{ query }}&page={{ sku_page }}" class="active">{{ sku_page }}</a>
{% else %}
<a href="/search?q={{ query }}&page={{ sku_page }}">{{ sku_page }}</a>
{% endif %}
{% endfor %}
{% if page.has_next %}
<a href="/search?q={{ query }}&page={{ page.next_page_number }}">下一页></a>
{% endif %}
</div>
</div>
然后就是我们首页的搜索,index.html中的搜索放在一个form表单中,action=”/search”是固定的,还有就是name=”q”也是固定的,详细如下:
<div class="search_con fl">
<form method="get" action="/search">{# 全文检索框架 固定search q #}
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>
</div>
最后就是重建索引文件:
在终端中输入:python manage.py rebuild_index或者使用 update_index命令。
成功以后就是浏览器页面搜索展示: