- 商品详情页
- 商品列表页
- 商品搜索
- 全文检索
- 安装和配置
- 索引文件生成
- 全文检索的使用
- 改变分词方式
新建detail.html
{% extends 'base_detail_list.html' %}
{% block title %}天天生鲜-商品详情{% endblock title %}
{% block main_content %}
{{ sku.name }}
{{ sku.desc }}
其他规格:
{% for sku in same_spu_skus %}
- {{ sku.name }}
{% endfor %}
总价:16.80元
新品推荐
{% for sku in new_skus %}
-
{{ sku.name }}
¥{{ sku.price }}
{% endfor %}
- 商品介绍
- 评论
- 商品详情:
- {{ sku.goods.detail|safe }}
{% for order in sku_orders %}
- 评论时间:{{ order.update_time }} 用户名:{{ order.order.user.username }}
- 评论内容:{{ order.comment }}
{% endfor %}
{% endblock main_content %}
{% block bottom %}
{% endblock bottom %}
{% block bottomfiles %}
{% endblock bottomfiles %}
view.py
# /goods/商品id
class DetailView(View):
'''详情页'''
def get(self, request, goods_id):
'''显示详情页'''
try:
sku = GoodsSKU.objects.get(id=goods_id)
except GoodsSKU.DoesNotExist:
# 商品不存在
return redirect(reverse('goods:index'))
# 获取商品的分类信息
types = GoodsType.objects.all()
# 获取商品的评论信息
sku_orders = OrderGoods.objects.filter(sku=sku).exclude(comment='')
# 获取新品信息
new_skus = GoodsSKU.objects.filter(type=sku.type).order_by('-create_time')[:2]
# 获取同一个SPU的其他规格商品
same_spu_skus = GoodsSKU.objects.filter(goods=sku.goods).exclude(id=goods_id)
# 获取用户购物车中商品的数目
user = request.user
cart_count = 0
if user.is_authenticated():
# 用户已登录
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
cart_count = conn.hlen(cart_key)
# 添加用户的历史记录
conn = get_redis_connection('default')
history_key = 'history_%d'%user.id
# 移除列表中的goods_id
conn.lrem(history_key, 0, goods_id)
# 把goods_id插入到列表的左侧
conn.lpush(history_key, goods_id)
# 只保存用户最新浏览的5条信息
conn.ltrim(history_key, 0, 4)
# 组织模板上下文
context = {'sku':sku, 'types':types,
'sku_orders':sku_orders,
'new_skus':new_skus,
'same_spu_skus':same_spu_skus,
'cart_count':cart_count}
# 使用模板
return render(request, 'detail.html', context)
商品列表页
修改list.html文件
{% extends 'base_detail_list.html' %}
{% block title %}天天生鲜-商品列表{% endblock title %}
{% block main_content %}
新品推荐
{% for sku in new_skus %}
-
{{ sku.name }}
¥{{ sku.price }}
{% endfor %}
{% for sku in skus_page %}
-
{{ sku.name }}
{% endfor %}
{% if skus_page.has_previous %}
<上一页
{% endif %}
{% for pindex in skus_page.paginator.page_range %}
{% if pindex == skus_page.number %}
{{ pindex }}
{% else %}
{{ pindex }}
{% endif %}
{% endfor %}
{% if skus_page.has_next %}
下一页>
{% endif %}
{% endblock main_content %}
定义view.py
分页导入
from django.core.paginator import Paginator
分页参考文档
https://doc.codingdict.com/django/topics/pagination.html
# 种类id 页码 排序方式
# restful api -> 请求一种资源
# /list?type_id=种类id&page=页码&sort=排序方式
# /list/种类id/页码/排序方式
# /list/种类id/页码?sort=排序方式
class ListView(View):
'''列表页'''
def get(self, request, type_id, page):
'''显示列表页'''
# 获取种类信息
try:
type = GoodsType.objects.get(id=type_id)
except GoodsType.DoesNotExist:
# 种类不存在
return redirect(reverse('goods:index'))
# 获取商品的分类信息
types = GoodsType.objects.all()
# 获取排序的方式 # 获取分类商品的信息
# sort=default 按照默认id排序
# sort=price 按照商品价格排序
# sort=hot 按照商品销量排序
sort = request.GET.get('sort')
if sort == 'price':
skus = GoodsSKU.objects.filter(type=type).order_by('price')
elif sort == 'hot':
skus = GoodsSKU.objects.filter(type=type).order_by('-sales')
else:
sort = 'default'
skus = GoodsSKU.objects.filter(type=type).order_by('-id')
# 对数据进行分页
paginator = Paginator(skus, 1)
# 获取第page页的内容
try:
page = int(page)
except Exception as e:
page = 1
if page > paginator.num_pages:
page = 1
# 获取第page页的Page实例对象
skus_page = paginator.page(page)
# todo: 进行页码的控制,页面上最多显示5个页码
# 获取新品信息
new_skus = GoodsSKU.objects.filter(type=type).order_by('-create_time')[:2]
# 获取用户购物车中商品的数目
user = request.user
cart_count = 0
if user.is_authenticated():
# 用户已登录
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
cart_count = conn.hlen(cart_key)
# 组织模板上下文
context = {'type':type, 'types':types,
'skus_page':skus_page,
'new_skus':new_skus,
'cart_count':cart_count,
'sort':sort}
# 使用模板
return render(request, 'list.html', context)
配置url
url(r'^list/(?P商品搜索 全文检索d+)/(?P d+)$', ListView.as_view(), name='list'), # 列表页
全文检索不同于特定字段的模糊查询,使用全文检索的效率更高,并且能够对于中文进行分词处理。
haystack:全文检索的框架,支持whoosh、solr、Xapian、Elasticsearc四种全文检索引擎,点击查看官方网站。
whoosh:纯Python编写的全文搜索引擎,虽然性能比不上sphinx、xapian、Elasticsearc等,但是无二进制包,程序不会莫名其妙的崩溃,对于小型的站点,whoosh已经足够使用,点击查看whoosh文档。
jieba:一款免费的中文分词包,如果觉得不好用可以使用一些收费产品。
- 安装python包。
pip install django-haystack
pip install whoosh - 在settings.py文件中注册应用haystack并做如下配置。
# 全文检索框架的配置
HAYSTACK_ConNECTIONS = {
'default': {
# 使用whoosh引擎
# 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
# 索引文件路径
'PATH': os.path.join(base_DIR, 'whoosh_index'),
}
}
# 当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# 指定搜索结果每页显示的条数
HAYSTACK_SEARCH_RESULTS_PER_PAGE=1
索引文件生成
- 在goods应用目录下新建一个search_indexes.py文件,在其中定义一个商品索引类。
# 定义索引类
from haystack import indexes
# 导入你的模型类
from goods.models import GoodsSKU
# 指定对于某个类的某些数据建立索引
# 索引类名格式:模型类名+Index
class GoodsSKUIndex(indexes.SearchIndex, indexes.Indexable):
# 索引字段 use_template=True指定根据表中的哪些字段建立索引文件的说明放在一个文件中
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()
-
在templates下面新建目录search/indexes/goods。
goods文件名为索引的类在的应用的名字
-
在此目录下面新建一个文件goodssku_text.txt并编辑内容如下。
命名规则:模型类名小写_text.txt
# 指定根据表中的哪些字段建立索引数据
{{ object.name }} # 根据商品的名称建立索引
{{ object.desc }} # 根据商品的简介建立索引
{{ object.goods.detail }} # 根据商品的详情建立索引
- 使用命令生成索引文件。
python manage.py rebuild_index
- 配置url
项目的url.py
2)表单搜索时设置表单内容如下。
点击标题进行提交时,会通过haystack搜索数据。
- 全文检索结果。
搜索出结果后,haystack会把搜索出的结果传递给templates/search目录下的search.html,传递的上下文包括:
query:搜索关键字
page:当前页的page对象 –>遍历page对象,获取到的是SearchResult类的实例对象,对象的属性object才是模型类的对象。
paginator:分页paginator对象
通过HAYSTACK_SEARCH_RESULTS_PER_PAGE 可以控制每页显示数量。
默认20条
创建search.html
{% extends 'base_detail_list.html' %}
{% block title %}天天生鲜-商品搜索结果列表{% endblock title %}
{% block main_content %}
{% for item in page %}
-
{{ item.object.name }}
{% endfor %}
{% if page.has_previous %}
<上一页
{% endif %}
{% for pindex in paginator.page_range %}
{% if pindex == page.number %}
{{ pindex }}
{% else %}
{{ pindex }}
{% endif %}
{% endfor %}
{% if spage.has_next %}
下一页>
{% endif %}
{% endblock main_content %}
改变分词方式
- 安装jieba分词模块。
pip install jieba - 找到虚拟环境py_django下的haystack目录。
/home/python/.virtualenvs/bj17_py3/lib/python3.5/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 -
打开复制出来的新文件,引入中文分析类,内部采用jieba分词。
from .ChineseAnalyzer import ChineseAnalyzer -
更改词语分析类。
查找
analyzer=StemmingAnalyzer()
改为
analyzer=ChineseAnalyzer() -
修改settings.py文件中的配置项。
-
重新创建索引数据
python manage.py rebuild_index



