商品详情页
新建detail.html
{% extends 'base_detail_list.html' %}
{% block title %}天天生鲜-商品详情{% endblock title %}
{% block main_content %}
<div class="breadcrumb">
<a href="#">全部分类</a>
<span>></span>
<a href="#">{
{ sku.type.name }}</a>
<span>></span>
<a href="#">商品详情</a>
</div>
<div class="goods_detail_con clearfix">
<div class="goods_detail_pic fl"><img src="{
{ sku.image.url }}"></div>
<div class="goods_detail_list fr">
<h3>{
{ sku.name }}</h3>
<p>{
{ sku.desc }}</p>
<div class="prize_bar">
<span class="show_pirze">¥<em>{
{ sku.price }}</em></span>
<span class="show_unit">单 位:{
{ sku.unite }}</span>
</div>
<div class="goods_num clearfix">
<div class="num_name fl">数 量:</div>
<div class="num_add fl">
<input type="text" class="num_show fl" value="1">
<a href="javascript:;" class="add fr">+</a>
<a href="javascript:;" class="minus fr">-</a>
</div>
</div>
<div>
<p>其他规格:</p>
<ul>
{% for sku in same_spu_skus %}
<li><a href="{% url 'goods:detail' sku.id %}">{
{ sku.name }}</a></li>
{% endfor %}
</ul>
</div>
<div class="total">总价:<em>16.80元</em></div>
<div class="operate_btn">
<a href="javascript:;" class="buy_btn">立即购买</a>
<a href="javascript:;" class="add_cart" id="add_cart">加入购物车</a>
</div>
</div>
</div>
<div class="main_wrap clearfix">
<div class="l_wrap fl clearfix">
<div class="new_goods">
<h3>新品推荐</h3>
<ul>
{% for sku in new_skus %}
<li>
<a href="{% url 'goods:detail' sku.id %}"><img src="{
{ sku.image.url }}"></a>
<h4><a href="{% url 'goods:detail' sku.id %}">{
{ sku.name }}</a></h4>
<div class="prize">¥{
{ sku.price }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="r_wrap fr clearfix">
<ul class="detail_tab clearfix">
<li class="active">商品介绍</li>
<li>评论</li>
</ul>
<div class="tab_content">
<dl>
<dt>商品详情:</dt>
<dd>{
{ sku.goods.detail|safe }}</dd>
</dl>
</div>
<div class="tab_content">
<dl>
{% for order in sku_orders %}
<dt>评论时间:{
{ order.update_time }} 用户名:{
{ order.order.user.username }}</dt>
<dd>评论内容:{
{ order.comment }}</dd>
{% endfor %}
</dl>
</div>
</div>
</div>
{% endblock main_content %}
{% block bottom %}
<div class="add_jump"></div>
{% endblock bottom %}
{% block bottomfiles %}
<script type="text/javascript" src="js/jquery-1.12.2.js"></script>
<script type="text/javascript">
var $add_x = $('#add_cart').offset().top;
var $add_y = $('#add_cart').offset().left;
var $to_x = $('#show_count').offset().top;
var $to_y = $('#show_count').offset().left;
$(".add_jump").css({
'left':$add_y+80,'top':$add_x+10,'display':'block'})
$('#add_cart').click(function(){
$(".add_jump").stop().animate({
'left': $to_y+7,
'top': $to_x+7},
"fast", function() {
$(".add_jump").fadeOut('fast',function(){
$('#show_count').html(2);
});
});
})
</script>
{% 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 %}
<div class="breadcrumb">
<a href="#">全部分类</a>
<span>></span>
<a href="#">{
{ type.name }}</a>
</div>
<div class="main_wrap clearfix">
<div class="l_wrap fl clearfix">
<div class="new_goods">
<h3>新品推荐</h3>
<ul>
{% for sku in new_skus %}
<li>
<a href="{% url 'goods:detail' sku.id %}"><img src="{
{ sku.image.url }}"></a>
<h4><a href="{% url 'goods:detail' sku.id %}">{
{ sku.name }}</a></h4>
<div class="prize">¥{
{ sku.price }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="r_wrap fr clearfix">
<div class="sort_bar">
<a href="{% url 'goods:list' type.id 1 %}" {% if sort == 'default' %}class="active"{% endif %}>默认</a>
<a href="{% url 'goods:list' type.id 1 %}?sort=price" {% if sort == 'price' %}class="active"{% endif %}>价格</a>
<a href="{% url 'goods:list' type.id 1 %}?sort=hot" {% if sort == 'hot' %}class="active"{% endif %}>人气</a>
</div>
<ul class="goods_type_list clearfix">
{% for sku in skus_page %}
<li>
<a href="{% url 'goods:detail' sku.id %}"><img src="{
{ sku.image.url }}"></a>
<h4><a href="{% url 'goods:detail' sku.id %}">{
{ sku.name }}</a></h4>
<div class="operate">
<span class="prize">¥{
{ sku.price }}</span>
<span class="unit">{
{ sku.price}}/{
{ sku.unite }}</span>
<a href="#" class="add_goods" title="加入购物车"></a>
</div>
</li>
{% endfor %}
</ul>
<div class="pagenation">
{% if skus_page.has_previous %}
<a href="{% url 'goods:list' type.id skus_page.previous_page_number %}?sort={
{ sort }}"><上一页</a>
{% endif %}
{% for pindex in skus_page.paginator.page_range %}
{% if pindex == skus_page.number %}
<a href="{% url 'goods:list' type.id pindex %}?sort={
{ sort }}" class="active">{
{ pindex }}</a>
{% else %}
<a href="{% url 'goods:list' type.id pindex %}?sort={
{ sort }}">{
{ pindex }}</a>
{% endif %}
{% endfor %}
{% if skus_page.has_next %}
<a href="{% url 'goods:list' type.id skus_page.next_page_number %}?sort={
{ sort }}">下一页></a>
{% endif %}
</div>
</div>
</div>
{% 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<type_id>\d+)/(?P<page>\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 %}
<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 pindex in paginator.page_range %}
{% if pindex == page.number %}
<a href="/search?q={
{ query }}&page={
{ pindex }}" class="active">{
{ pindex }}</a>
{% else %}
<a href="/search?q={
{ query }}&page={
{ pindex }}">{
{ pindex }}</a>
{% endif %}
{% endfor %}
{% if spage.has_next %}
<a href="/search?q={
{ query }}&page={
{ page.next_page_number }}">下一页></a>
{% endif %}
</div>
</div>
{% 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