一、商品详情页
- 商品详情页依然采用叶敏静态化处理
- 商品详情页的静态化有运营人员在编辑商品信息时触发生成静态化页面。
1.实现静态化异步任务:
- 在celery_tasks中新建html/tesks.py任务:
# meiduo_mall/celery_tasks/html/task.py
@ celery_app.task(name='generate_static_sku_detail_html')
def generate_static_sku_detail_html(sku_id):"""生成静态商品详情页面:param sku_id: 商品sku_id:return:"""# 商品分类信息categories = get_categories()# 获取当前sku的信息sku = SKU.objects.filter(id=sku_id)sku.images = sku.skuimage_set.all()# 面包屑导航信息中的频道goods = sku.goodsgoods.channel = goods.category1.goodschannel_set.all()[0]# 构建当前商品的规格键# sku_key = [规格1参数id, 规格2参数id, 规格3参数id, ...]sku_specs = sku.skuspecification_set.order_by('spec_id')sku_key = []for spec in sku_specs:sku_key.append(spec.option.id)# 构建当前商品的所有SKUskus = goods.sku_set.all()# 构建不同规格参数(选项)的sku字典# spec_sku_map = {# (规格1参数id, 规格2参数id, 规格3参数id, ...): sku_id,# (规格1参数id, 规格2参数id, 规格3参数id, ...): sku_id,# ...# }spec_sku_map = {}for s in skus:# 获取sku的规格参数s_specs = s.skuspecification_set.order_by('spec_id')# 用于形成规格参数-sku字典的keykey = []for spec in s_specs:key.append(spec)# 向规格参数-sku字典添加数据spec_sku_map[tuple(key)] = s.id# 获取当前商品的规格信息# specs = [# {# 'name': '屏幕尺寸',# 'options': [# {'value': '13.3寸', 'sku_id': xxx},# {'value': '15.4寸', 'sku_id': xxx},# ]# },# {# 'name': '颜色',# 'options': [# {'value': '银色', 'sku_id': xxx},# {'value': '黑色', 'sku_id': xxx}# ]# },# ...# ]specs = goods.goodsspecification_set.order_by("id")# 若当前sku的过个信息不完整,则不继续添加if len(sku_key) < len(specs):returnfor index, spec in enumerate(specs):# 重复当前sku的规格键key = sku_key[:]# 该规格的选项options = spec.specificationoption_set.all()for option in options:# 在规格参数sku字典中查找符合当前规格的skukey[index] = option.idoption.sku_id = spec_sku_map.get(tuple(key))spec.options = options# 渲染模板,生成静态html文件context = {'categories': categories,'goods': goods,'specs': specs,'sku': sku}template = loader.get_template('detail.html')html_text = template.render(context)file_path = os.path.join(settings.GENERATED_STATIC_HTML_FILES_DIR, 'goods/' + str(sku_id) + '.html')with open(file_path, 'w') as f:f.write(html_text)
2.页面模板准备:
<!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">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>美多商城-商品详情</title><link rel="stylesheet" type="text/css" href="/css/reset.css"><link rel="stylesheet" type="text/css" href="/css/main.css"><script type="text/javascript" src="/js/host.js"></script><script type="text/javascript" src="/js/vue-2.5.16.js"></script><script type="text/javascript" src="/js/axios-0.18.0.min.js"></script>
</head>
<body><div id="app" v-cloak><div class="header_con"><div class="header"><div class="welcome fl">欢迎来到美多商城!</div><div class="fr"><div v-if="username" class="login_btn fl">欢迎您:<em>[[ username ]]</em><span>|</span><a @click="logout">退出</a></div><div v-else class="login_btn fl"><a href="login.html">登录</a><span>|</span><a href="register.html">注册</a></div><div class="user_link fl"><span>|</span><a href="/user_center_info.html">用户中心</a><span>|</span><a href="/cart.html">我的购物车</a><span>|</span><a href="/user_center_order.html">我的订单</a></div></div></div></div><div class="search_bar clearfix"><a href="/index.html" class="logo fl"><img src="/images/logo.png"></a><div class="search_wrap fl"><form method="get" action="/search.html" class="search_con"><input type="text" class="input_text fl" name="q" placeholder="搜索商品"><input type="submit" class="input_btn fr" name="" value="搜索"></form><ul class="search_suggest fl"><li><a href="#">索尼微单</a></li><li><a href="#">优惠15元</a></li><li><a href="#">美妆个护</a></li><li><a href="#">买2免1</a></li></ul></div><div class="guest_cart fr"><a href="#" class="cart_name fl">我的购物车</a><div class="goods_count fl" id="show_count">15</div><ul class="cart_goods_show"><li><img src="images/goods/goods001.jpg" alt="商品图片"><h4>商品名称手机</h4><div>4</div></li><li><img src="images/goods/goods002.jpg" alt="商品图片"><h4>商品名称手机</h4><div>5</div></li><li><img src="images/goods/goods003.jpg" alt="商品图片"><h4>商品名称手机</h4><div>6</div></li><li><img src="images/goods/goods003.jpg" alt="商品图片"><h4>商品名称手机</h4><div>6</div></li></ul> </div></div><div class="navbar_con"><div class="navbar"><div class="sub_menu_con fl"><h1 class="fl">商品分类</h1><ul class="sub_menu">{% for group in categories.values %}<li><div class="level1">{% for channel in group.channels %}<a href="{{ channel.url }}">{{ channel.name }}</a>{% endfor %}</div><div class="level2">{% for cat2 in group.sub_cats %}<div class="list_group"><div class="group_name fl">{{cat2.name}} ></div><div class="group_detail fl">{% for cat3 in cat2.sub_cats %}<a href="/list.html?cat={{cat3.id}}">{{cat3.name}}</a>{% endfor %}</div></div>{% endfor %}</div></li>{% endfor %}</ul></div><ul class="navlist fl"><li><a href="">首页</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="{{ goods.channel.url }}">{{ goods.category1.name }}</a><span>></span><span>{{ goods.category2.name }}</span><span>></span><a href="/list.html?cat={{ goods.category3.id }}">{{goods.category3.name }}</a></div><div class="goods_detail_con clearfix"><div class="goods_detail_pic fl"><img src="{{ sku.default_image_url }}"></div><div class="goods_detail_list fr"><h3>{{ sku.name }}</h3><p>{{ sku.caption }}</p><div class="prize_bar"><span class="show_pirze">¥<em>{{ sku.price }}</em></span><span> 市场价¥{{sku.market_price}}</span><a href="javascript:;" class="goods_judge">{{ sku.comments }}人评价</a></div><div class="goods_num clearfix"><div class="num_name fl">数 量:</div><div class="num_add fl"><input v-model="sku_count" type="text" class="num_show fl"><a @click="sku_count++" class="add fr">+</a><a @click="on_minus()" class="minus fr">-</a></div></div>{% for spec in specs %}<div class="type_select"><label>{{ spec.name }}:</label>{% for option in spec.options %}{% if option.sku_id == sku.id %}<a href="javascript:;" class="select">{{ option.value }}</a>{% elif option.sku_id %}<a href="/goods/{{option.sku_id}}.html">{{ option.value }}</a>{% else %}<a href="javascript:;">{{ option.value }}</a>{% endif %}{% endfor %}</div>{% endfor %}<div class="total">总价:<em>[[sku_amount]]元</em></div><div class="operate_btn"><a @click="add_cart" 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><li v-for="sku in hots"><a :href="sku.url"><img :src="sku.default_image_url"></a><h4><a :href="sku.url">[[sku.name]]</a></h4><div class="prize">¥[[sku.price]]</div></li></ul></div></div><div class="r_wrap fr clearfix"><ul class="detail_tab clearfix"><li @click="on_tab_content('detail')" :class="tab_content.detail?'active':''">商品详情</li><li @click="on_tab_content('pack')" :class="tab_content.pack?'active':''">规格与包装</li><li @click="on_tab_content('comment')" :class="tab_content.comment?'active':''">商品评价([[comments.length]])</li><li @click="on_tab_content('service')" :class="tab_content.service?'active':''">售后服务</li></ul><div @click="on_tab_content('detail')" class="tab_content" :class="tab_content.detail?'current':''"><dl><dt>商品详情:</dt><dd>{{ goods.desc_detail|safe }}</dd></dl></div><div @click="on_tab_content('pack')" class="tab_content" :class="tab_content.pack?'current':''"><dl><dt>规格与包装:</dt><dd>{{ goods.desc_pack|safe }}</dd></dl></div><div @click="on_tab_content('comment')" class="tab_content" :class="tab_content.comment?'current':''"><ul class="judge_list_con"><li class="judge_list fl" v-for="comment in comments"><div class="user_info fl"><b>[[comment.username]]</b></div><div class="judge_info fl"><div :class="comment.score_class"></div><div class="judge_detail">[[comment.comment]]</div></div></li></ul></div><div @click="on_tab_content('service')" class="tab_content" :class="tab_content.service?'current':''"><dl><dt>售后服务:</dt><dd>{{ goods.desc_service|safe }}</dd></dl></div></div></div><div class="footer"><div class="foot_link"><a href="#">关于我们</a><span>|</span><a href="#">联系我们</a><span>|</span><a href="#">招聘人才</a><span>|</span><a href="#">友情链接</a></div><p>CopyRight © 2016 北京美多商业股份有限公司 All Rights Reserved</p><p>电话:010-****888 京ICP备*******8号</p></div></div><script type="text/javascript">var price = {{sku.price}};var cat = {{ goods.category3.id }};</script><script type="text/javascript" src="/js/detail.js"></script></body>
</html>
3. 页面的detail.js
var vm = new Vue({el: '#app',delimiters: ['[[', ']]'],data: {host,username: sessionStorage.username || localStorage.username,user_id: sessionStorage.user_id || localStorage.user_id,token: sessionStorage.token || localStorage.token,tab_content: {detail: true,pack: false,comment: false,service: false},sku_id: '',sku_count: 1,sku_price: price,cart_total_count: 0, // 购物车总数量cart: [], // 购物车数据hots: [], // 热销商品cat: cat, // 商品类别comments: [], // 评论信息score_classes: {1: 'stars_one',2: 'stars_two',3: 'stars_three',4: 'stars_four',5: 'stars_five',}},computed: {sku_amount: function(){return (this.sku_price * this.sku_count).toFixed(2);}},mounted: function(){// 添加用户浏览历史记录this.get_sku_id();this.get_cart();this.get_hot_goods();this.get_comments();},methods: {// 退出logout: function(){sessionStorage.clear();localStorage.clear();location.href = '/login.html';},// 控制页面标签页展示on_tab_content: function(name){this.tab_content = {detail: false,pack: false,comment: false,service: false};this.tab_content[name] = true;},// 从路径中提取sku_idget_sku_id: function(){var re = /^\/goods\/(\d+).html$/;this.sku_id = document.location.pathname.match(re)[1];},// 减小数值on_minus: function(){if (this.sku_count > 1) {this.sku_count--;}},// 添加购物车add_cart: function(){},// 获取购物车数据get_cart: function(){},// 获取热销商品数据get_hot_goods: function(){},// 获取商品评价信息get_comments: function(){}}
});
4. 异步任务的触发
运营人员在Admin站点保存商品信息时,应该触发生成商品静态页的异步任务。
我们需要调整Admin站点保存和删除商品信息时行为。
在Admin站点保存或删除数据时,Django是调用的Admin站点管理器类的save_model()方法和delete_model()方法,我们只需重新实现这两个方法,在这两个方法中调用异步任务即可。
编辑goods/admin.py
# meiduo_mall/apps/goods/admin.py
from django.contrib import admin# Register your models here.
from goods.models import GoodsCategory, Brand, Goods, SKU, GoodsChannel, GoodsSpecification, SpecificationOption, \SKUSpecification, SKUImageclass GoodsCategoryAdmin(admin.ModelAdmin):def save_model(self, request, obj, form, change):obj.save()from celery_tasks.html.tasks import generate_static_list_search_htmlgenerate_static_list_search_html.delay()def delete_model(self, request, obj):obj.delete()from celery_tasks.html.tasks import generate_static_list_search_htmlgenerate_static_list_search_html.delay()class SKUAdmin(admin.ModelAdmin):"""SKU的管理"""def save_model(self, request, obj, form, change):"""保存数据:param request::param obj::param form::param change::return:"""obj.save()from celery_tasks.html.tasks import generate_static_sku_detail_htmlgenerate_static_sku_detail_html.delay(obj.id)class SKUSpecificationAdimn(admin.ModelAdmin):"""sku规格"""def save_model(self, request, obj, form, change):obj.save()from celery_tasks.html.tasks import generate_static_sku_detail_htmlgenerate_static_sku_detail_html.delay(obj.sku.id)def delete_model(self, request, obj):sku_id = obj.sku.idobj.delete()from celery_tasks.html.tasks import generate_static_sku_detail_htmlgenerate_static_sku_detail_html.delay(sku_id)class SKUImageAdmin(admin.ModelAdmin):"""图片管理"""def save_model(self, request, obj, form, change):obj.save()from celery_tasks.html.tasks import generate_static_sku_detail_htmlgenerate_static_sku_detail_html.delay(obj.sku.id)# 设置SKU默认图片sku = obj.skuif not sku.default_image_url:sku.default_image_url = obj.image.urlsku.save()def delete_model(self, request, obj):sku_id = obj.sku.idobj.delete()from celery_tasks.html.tasks import generate_static_sku_detail_htmlgenerate_static_sku_detail_html.delay(sku_id)admin.site.register(GoodsCategory, GoodsCategoryAdmin)
admin.site.register(GoodsChannel)
admin.site.register(Goods)
admin.site.register(Brand)
admin.site.register(GoodsSpecification)
admin.site.register(SpecificationOption)
admin.site.register(SKU, SKUAdmin)
admin.site.register(SKUSpecification, SKUSpecificationAdimn)
admin.site.register(SKUImage, SKUImageAdmin)
二、面包屑导航:
1.什么是面包屑导航:
面包屑导航的作用是告诉访问者他们目前在网站中的位置以及如何返回。
2.接口分析:
请求方式: GET /categories/(?P<GoodsCategory_id>)/
请求参数: 路径传参
参数 | 类型 | 是否必须 | 说明 |
---|---|---|---|
GoodsCategory_id | int | 是 | 三级分类id |
返回值: JSON
参数 | 类型 | 是否必须 | 说明 |
---|---|---|---|
cat1 | str | 否 | 一级分类名 |
cat2 | str | 否 | 二级分类名 |
cat3 | str | 否 | 三级分类名 |
实例:
{"cat1": 手机,"cat2": 手机通讯,"cat3": 手机
}
后端实现:
# meiduo_mall/apps/goods/views.py
...
class CategoriesView(APIView):"""获取当前分类信息"""def get(self,request, pk):"""1.获取前端数据2. 查询当前三级分类信息3.通过三级分类信息获取一二集分类4. 返回:param request::return:"""cat3 = GoodsCategory.objects.get(id=pk) # 获取三级cat2 = cat3.parent # 自关联获取二级,cat1 = cat2.parent # 自关联获取一级# 返回数据return Response({"cat1": cat1.name,"cat2": cat2.name,"cat3": cat3.name})
添加路由:
# meiduo_mall/apps/goods/urls.py
...
urlpatterns = [...url(r"^categories/(?P<pk>\d+)/$", views.CategoriesView.as_view()), # 面包屑导航
]