python学习之美多商城(十六):商品部分:商品详情页(详情页面静态化)

版权声明:浅弋、璃鱼的原创 https://blog.csdn.net/qq_35709559/article/details/86655792

一、商品详情页

  • 商品详情页依然采用叶敏静态化处理
  • 商品详情页的静态化有运营人员在编辑商品信息时触发生成静态化页面。

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.goods
    goods.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)

    # 构建当前商品的所有SKU
    skus = 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
        key = []
        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):
        return

    for index, spec in enumerate(specs):
        # 重复当前sku的规格键
        key = sku_key[:]
        # 该规格的选项
        options = spec.specificationoption_set.all()
        for option in options:
            # 在规格参数sku字典中查找符合当前规格的sku
            key[index] = option.id
            option.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}} &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>

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_id
        get_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, SKUImage


class GoodsCategoryAdmin(admin.ModelAdmin):

    def save_model(self, request, obj, form, change):
        obj.save()
        from celery_tasks.html.tasks import generate_static_list_search_html
        generate_static_list_search_html.delay()

    def delete_model(self, request, obj):
        obj.delete()
        from celery_tasks.html.tasks import generate_static_list_search_html
        generate_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_html
        generate_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_html
        generate_static_sku_detail_html.delay(obj.sku.id)


    def delete_model(self, request, obj):
        sku_id = obj.sku.id
        obj.delete()
        from celery_tasks.html.tasks import generate_static_sku_detail_html
        generate_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_html
        generate_static_sku_detail_html.delay(obj.sku.id)

        # 设置SKU默认图片
        sku = obj.sku
        if not sku.default_image_url:
            sku.default_image_url = obj.image.url
            sku.save()

    def delete_model(self, request, obj):
        sku_id = obj.sku.id
        obj.delete()
        from celery_tasks.html.tasks import generate_static_sku_detail_html
        generate_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)

猜你喜欢

转载自blog.csdn.net/qq_35709559/article/details/86655792