Django 页面静态化 商品详情页静态化

news/2024/11/6 11:38:17/

商品详情页

商品详情页依然采用页面静态化技术。

商品详情页的静态化由运营人员在编辑商品信息时触发生成静态化页面。

先来实现静态化异步任务,在celery_tasks中新建html/tasks.py任务

from celery_tasks.main import celery_app
from django.template import loader
from django.conf import settings
import osfrom goods.utils import get_categories
from goods.models import SKU@celery_app.task(name='generate_static_sku_detail_html')
def generate_static_sku_detail_html(sku_id):"""生成静态商品详情页面:param sku_id: 商品sku id"""# 商品分类菜单categories = get_categories()# 获取当前sku的信息sku = SKU.objects.get(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字典的键key = []for spec in s_specs:key.append(spec.option.id)# 向规格参数-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)

将形成商品类别部分的数据封装成一个公共函数,放在goods/utils.py中

from collections import OrderedDictfrom goods.models import GoodsChanneldef get_categories():"""获取商城商品分类菜单:return 菜单字典"""# 商品频道及分类菜单# 使用有序字典保存类别的顺序# categories = {#     1: { # 组1#         'channels': [{'id':, 'name':, 'url':},{}, {}...],#         'sub_cats': [{'id':, 'name':, 'sub_cats':[{},{}]}, {}, {}, ..]#     },#     2: { # 组2##     }# }categories = OrderedDict()channels = GoodsChannel.objects.order_by('group_id', 'sequence')for channel in channels:group_id = channel.group_id  # 当前组if group_id not in categories:categories[group_id] = {'channels': [], 'sub_cats': []}cat1 = channel.category  # 当前频道的类别# 追加当前频道categories[group_id]['channels'].append({'id': cat1.id,'name': cat1.name,'url': channel.url})# 构建当前类别的子类别for cat2 in cat1.goodscategory_set.all():cat2.sub_cats = []for cat3 in cat2.goodscategory_set.all():cat2.sub_cats.append(cat3)categories[group_id]['sub_cats'].append(cat2)return categories

页面模板

<!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="#" alt="商品图片"><h4>商品名称手机</h4><div>4</div></li><li><img src="#" alt="商品图片"><h4>商品名称手机</h4><div>5</div></li><li><img src="#" alt="商品图片"><h4>商品名称手机</h4><div>6</div></li><li><img src="#" 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}} &gt;</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>

页面的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(){}}
});

异步任务的触发

运营人员在Admin站点保存商品信息时,应该触发生成商品静态页的异步任务。

我们需要调整Admin站点保存和删除商品信息时行为。

在Admin站点保存或删除数据时,Django是调用的Admin站点管理器类的save_model()方法和delete_model()方法,我们只需重新实现这两个方法,在这两个方法中调用异步任务即可。

编辑goods/admin.py

from django.contrib import admin# Register your models here.from goods import modelsclass SKUAdmin(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.id)class SKUSpecificationAdmin(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)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(models.GoodsCategory)
admin.site.register(models.GoodsChannel)
admin.site.register(models.Goods)
admin.site.register(models.Brand)
admin.site.register(models.GoodsSpecification)
admin.site.register(models.SpecificationOption)
admin.site.register(models.SKU, SKUAdmin)
admin.site.register(models.SKUSpecification, SKUSpecificationAdmin)
admin.site.register(models.SKUImage, SKUImageAdmin)

脚本工具

为了开发方便,我们还可以编写手动生成所有商品静态页面的脚本regenerate_detail_html.py

#!/usr/bin/env python"""
功能:手动生成所有SKU的静态detail html文件
使用方法:./regenerate_detail_html.py
"""
import sys
sys.path.insert(0, '../')# 设置Django运行所依赖的环境变量
import os
if not os.getenv('DJANGO_SETTINGS_MODULE'):os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_mall.settings.dev'# 让Django进行一次初始化
import django
django.setup()from django.template import loader
from django.conf import settingsfrom goods.utils import get_categories
from goods.models import SKUdef generate_static_sku_detail_html(sku_id):"""生成静态商品详情页面:param sku_id: 商品sku id"""# 商品分类菜单categories = get_categories()# 获取当前sku的信息sku = SKU.objects.get(id=sku_id)sku.images = sku.skuimage_set.all()# 面包屑导航信息中的频道goods = sku.goodsgoods.channel = goods.category1.goodschannel_set.all()[0]# 构建当前商品的规格键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字典的键key = []for spec in s_specs:key.append(spec.option.id)# 向规格参数-sku字典添加记录spec_sku_map[tuple(key)] = s.id# 获取当前商品的规格信息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)if __name__ == '__main__':skus = SKU.objects.all()for sku in skus:print(sku.id)generate_static_sku_detail_html(sku.id)

http://www.ppmy.cn/news/206573.html

相关文章

python学习之美多商城(十八):商品部分:商品详情页(详情页面静态化)、面包屑导航

一、商品详情页 商品详情页依然采用叶敏静态化处理商品详情页的静态化有运营人员在编辑商品信息时触发生成静态化页面。 1.实现静态化异步任务: 在celery_tasks中新建html/tesks.py任务: # meiduo_mall/celery_tasks/html/task.pycelery_app.task(namegenerate_static_sku_…

秒杀系统HTML倒计时设置

倒计时就是在秒杀表中设定好要秒杀的时间 再减去现在的时间 把时间差显示在页面上 由于第一次搞秒杀倒计时 辛苦搞了半天 留此微博 以留备用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-…

破壳2周造百万销量,荣耀20系列:三个超级炸弹的“寒潮逆袭”

在5G尚未正式商用前&#xff0c;智能手机行业显得有些青黄不接&#xff0c;甚至稍显沉闷。好在&#xff0c;总有玩家可以“平地一声雷”&#xff0c;打破这样的沉闷。5月31日发布的荣耀20系列&#xff0c;就在上市后的短短2周内&#xff0c;创造了多个超级炸弹。 三大“超级炸弹…

Python_美多商城(商品)_6

商品数据库表设计 SPU和SKU 在电商中对于商品&#xff0c;有两个重要的概念&#xff1a;SPU和SKU 1. SPU介绍 SPU Standard Product Unit &#xff08;标准产品单位&#xff09; SPU是商品信息聚合的最小单位&#xff0c;是一组可服用、易检索的标准化信息的集合&#xff0c…

基于几何学习图像的三维重建发展_【干货】基于深度学习的多视图几何:从监督学习到无监督学习...

本文整理自戴玉超教授在第二届SLAM技术论坛中的报告&#xff1a;《基于深度学习的多视图几何&#xff1a;从监督学习到无监督学习》&#xff0c;共5700余字。 几何视觉利用相机获取的多视角图像重建所观测场景的三维几何结构&#xff0c;在SLAM、无人系统、自动驾驶、机器人、虚…

Django项目实战——13—(自定义Django文件存储类、商品列表页、列表页面包屑导航、列表页分页和排序)

1、自定义Django文件存储类 上一篇博客中介绍了首页轮播图的显示&#xff0c;由于因为域名的问题&#xff0c;图片无法访问虚拟机中的storage容器中存储的data数据。 结论&#xff1a; 通过FastDFS上传文件后返回的Remote file_id字段是文件索引。文件索引会被我们存储到MySQ…

二十六

首页新闻博问专区闪存班级 我的博客我的园子账号设置退出登录注册登录BWH_Steven 博客园首页新随笔联系订阅管理随笔 - 112 文章 - 0 评论 - 20【设计模式】第七篇&#xff1a;和我一起简单认识桥接模式 【设计模式】第七篇&#xff1a;和我一起简单…

我又准备转行了

说一些心里话。 公众号最近这两个月我更新地很少&#xff0c;一是懒&#xff0c;二是忙&#xff0c;三是心里一直在想一个问题&#xff1a;我以后要写些什么内容。 想了很久&#xff0c;到今天我想明白了&#xff0c;那就是去写我真正喜欢和擅长的内容&#xff1a;摄影和旅行。…