• 05 商品模块


    商品主页页面

    商品主页页面的前端页面 效果图如下:

     

    后端视图的业务逻辑处理:

    根据前端展示的页面,后端需要向前端传送的数据有:

      1 后端需要想前端传送的数据有
      2 全部商品额分类信息
      3 轮播图的数据
      4 广告的信息
      5 分类商品展示的标题和图片
      6 用户购物车的信息

    视图 IndexView 函数的代码如下:

    from django.shortcuts import render
    from django.views.generic import View
    from .models import GoodsCategory,IndexGoodsBanner,IndexPromotionBanner
    from .models import IndexCategoryGoodsBanner
    
    class IndexView(View):
        def get(self,request):
            # 后端需要想前端传送的数据有
            # 全部商品额分类信息
            # 轮播图的数据
            # 广告的信息
            # 分类商品展示的标题和图片
            # 用户购物车的信息
    
            # 获取所有的商品分类
            goods_cate=GoodsCategory.objects.all()
            # 获取轮播图信息
            index_banner = IndexGoodsBanner.objects.all().order_by("index")
            # 获取广告的信息
            promotinon_banners=IndexPromotionBanner.objects.all().order_by("index")
    
            for category in goods_cate:
                # 主页分类商品的标题
                category_title=IndexCategoryGoodsBanner.objects.filter(category=category,display_type=0)[:4]
                category.title = category_title
                category_image=IndexCategoryGoodsBanner.objects.filter(category=category,display_type=1)[:4]
                category.img = category_image
    
            # 先把购物车的数量 设置为0
            cart_num = 0
            context = {
                "goods_cate":goods_cate,
                "index_banners":index_banner,
                "promotinon_banners":promotinon_banners,
                "cart_num":cart_num
            }
    
    
            return render(request,'index.html',context)
    View Code

    前端模板商品分类要填充的代码如下:

    <ul class="subnav fl">
        {% for cate in goods_cate %}
           <li><a href="#model0{{ forloop.counter }}" class="{{ cate.logo }}">{{ cate.name }}</a></li>
        {% endfor %}
    </ul>
    View Code

    前端模板轮播图要填充的代码如下: 

    <ul class="slide_pics">
                    {% for slide in index_banners %}
                    <li><a href="#"><img src="{{ slide.image.url }}" alt="幻灯片"></a></li>
                    {% endfor %}
                </ul>
    View Code

    前端模板活动广告要填充的代码如下:

    <div class="adv fl">
       {% for add in promotinon_banners %}
          <a href="{{ add.url }}"><img src="{{ add.image.url }}"></a>
        {% endfor %}
    
    </div>
    View Code

    前端模板商品分类展示要填充的代码如下:

    {% for cate in goods_cate %}
            <div class="list_model">
            <div class="list_title clearfix">
                <h3 class="fl" id="model01">{{ cate.name }}</h3>
                <div class="subtitle fl">
                    <span>|</span>
                    {% for title in cate.title %}
                        <a href="#">{{ title.sku.title }}</a>
                    {% endfor %}
                </div>
                <a href="#" class="goods_more fr" id="fruit_more">查看更多 ></a>
            </div>
    
            <div class="goods_con clearfix">
                <div class="goods_banner fl"><img src="{{ cate.image.url }}"></div>
                <ul class="goods_list fl">
                    {% for img in cate.img %}
                        <li>
                        <h4><a href="#">{{ img.sku.name }}</a></h4>
                        <a href="#"><img src="{{ img.sku.default_image.url }}"></a>
                        <div class="prize">{{ img.sku.price }}</div>
                    </li>
                    {% endfor %}
                </ul>
            </div>
        </div>
        {% endfor %}
    View Code

    可以在前端中判断用户是否登陆,如果登陆出现欢迎访问,没有则是登陆和注册

    {% if user.is_authenticated %}
       <div class="login_btn fl">
             欢迎您:<em>{{ user.username }}</em>
    	<span>|</span>
    	<a href="{% url 'users:logout' %}">退出</a>
       </div>
     {% else %}
        <div class="login_btn fl">
    	<a href="{% url 'users:login' %}">登录</a>
    	<span>|</span>
    	<a href="{% url 'users:register' %}">注册</a>
       </div>
    {% endif %}

    在浏览器中输入,就可以显示后端填充的信息

    http://127.0.0.1:8000

     页面静态化(重点)

    把动态模板的页面渲染一次后,把结果保存下来,存成一个静态的html文件,就是页面静态化,对于不经常变化的动态页面可以做静态化处理。

    进行静态化的触发: 运营人员修改主页动态数据的时候,进行静态化处理,就是运营人员修改数据的时候,将生成静态化的过程交给celery异步完成,不影响任何其他业务。

    在celery_tasks/tasks.py中定义生成 静态化文件的任务

    from django.core.mail import send_mail
    from django.conf import settings
    from django.template import loader
    from goods.models import GoodsCategory, IndexGoodsBanner, IndexPromotionBanner
    from goods.models import IndexCategoryGoodsBanner
    
    
    
    @app.task
    def generate_static_index_html():
        """生成主页的静态html文件"""
        # 获取所有的商品分类
        goods_cate = GoodsCategory.objects.all()
        # 获取轮播图信息
        index_banner = IndexGoodsBanner.objects.all().order_by("index")
        # 获取广告的信息
        promotinon_banners = IndexPromotionBanner.objects.all().order_by("index")
    
        for category in goods_cate:
            # 主页分类商品的标题
            category_title = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=0)[:4]
            category.title = category_title
            category_image = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=1)[:4]
            category.img = category_image
    
        # 先把购物车的数量 设置为0
        cart_num = 0
        context = {
            "goods_cate": goods_cate,
            "index_banners": index_banner,
            "promotinon_banners": promotinon_banners,
            "cart_num": cart_num
        }
    
        # 加载模板
        template = loader.get_template("static_index.html")
        # 渲染模板,生成html数据
        html_data = template.render(context)
        # 保存产生的静态html数据
        file_path = os.path.join(settings.STATICFILES_DIRS[0], "index.html")
        with open(file_path, "w") as f:
            f.write(html_data)
    View Code 

    对以上的局部代码进行解释

    from django.template import loader
    
    # 加载模板
        template = loader.get_template("static_index.html")
        # 渲染模板,生成html数据
        html_data = template.render(context)
        # 保存产生的静态html数据
        file_path = os.path.join(settings.STATICFILES_DIRS[0], "index.html")
        with open(file_path, "w") as f:
            f.write(html_data)
    

    把模板渲染过后的数据,写入到index.html文件中,当浏览器访问主页的时候就会返回静态话的文件,减少服务器的压力。

    用于静态的话的文件是给未登录的用户使用的,所以在templates中把index.html文件重新复制一份在当前的目录下,并命名为static_index.html,

    把static_index.html中的判断用户是否登陆的代码删掉,只留用户注册的:

    <div class="login_btn fl">
    	<a href="{% url 'users:login' %}">登录</a>
    	<span>|</span>
    	<a href="{% url 'users:register' %}">注册</a>
    </div>

     以前的如下所示:

    把当前的项目复制到家目录下(python)

    修改nginx的配置文件

    sudo vim /usr/local/nginx/conf/nginx.conf

     

     重启niginx服务器

     sudo /usr/local/nginx/sbin/nginx -s reload 

    开启celery服务器

    cd 
    
    celery -A celery_tasks.tasks worker --loglevel=info

    在python manager.py shell 中测试

    from celery_tasks.tasks import  generate_static_index_html
    generate_static_index_html.delay()
    

    celery会收到任务

     

    在python/ttsx/static会生成一个index.html文件

     在浏览器中输入本机的ip地址,会调用静态化的文件

    http://192.168.228.135/

     

    把django中动态处理的页面和nginx的静态页面区分开来,所以把商品页的路径改成另外一个:

    url(r'^index$',views.IndexView.as_view(),name='index')

    通过改写admin的管理类调用生成静态页面的异步任务

    当运营人员对模型类,进行添加和修改的时候就会触发celery的任务生成静态的话的文件

    通过添加save_model 和delete_model的方法,只要一触发就会调用celery

    在gooods应用中的admin中添加管理器的方法;

    from django.contrib import admin
    
    from goods.models import IndexGoodsBanner, IndexCategoryGoodsBanner, IndexPromotionBanner
    from goods.models import GoodsCategory
    from celery_tasks.tasks import generate_static_index_html
    
    
    class BaseModel(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            obj.save()
            generate_static_index_html.delay()
            
        def delete_model(self, request, obj):
            """admin站点在模型删除数据的时候调用"""
            # 从数据库中删除
            obj.delete()
    
            # 调用生成静态页面的celery异步任务
            generate_static_index_html.delay()
    
    class IndexPromotionBannerAdmin(BaseModel):
        pass
    class GoodsCategoryAdmin(BaseModel):
        """商品分类信息的管理类"""
        # 在这里填充控制amdin站点的展示效果
        pass
    
    admin.site.register(IndexPromotionBanner,IndexPromotionBannerAdmin)
    admin.site.register(GoodsCategory, GoodsCategoryAdmin)
    View Code

    在后台管理页面把主页促销活动的盛夏的顺序改为2,保存

    一开始的静态化的顺序

    触发celery生成静态化的顺序

    使用缓存(重点)

    把备份的数据放到内存中,如果下次访问的时候,如果内存中有就从内存中获取,如果内存中没有则django动态的计算数据,然后在设置到缓存中供下次使用

    django关于缓存的文档:http://python.usyiyi.cn/translate/django_182/topics/cache.html

    这里我使用的是,访问缓存

    你可以通过 类字典对象django.core.cache.caches.访问配置在CACHES 设置中的字典类对象

    最基本的接口是 set(key, value, timeout) 和 get(key):

    get(key) 如果没有取到值则会返回None

    from django.core.cache import cache
    >>> cache.set('my_key', 'hello, world!', 30)
    >>> cache.get('my_key')
    'hello, world!

    在IndexView的视图中添加缓存后的代码如下:

    from django.core.cache import cache
    
    class IndexView(View):
        def get(self,request):
            # 后端需要想前端传送的数据有
            # 全部商品额分类信息
            # 轮播图的数据
            # 广告的信息
            # 分类商品展示的标题和图片
            # 用户购物车的信息
    
            # 先从缓存中取数据
            context=cache.get("index_page_data")
            if context is None:
                print("没有用上缓存的数据,从数据库中查询")
                # 获取所有的商品分类
                goods_cate=GoodsCategory.objects.all()
                # 获取轮播图信息
                index_banner = IndexGoodsBanner.objects.all().order_by("index")
                # 获取广告的信息
                promotinon_banners=IndexPromotionBanner.objects.all().order_by("index")
    
                for category in goods_cate:
                    # 主页分类商品的标题
                    category_title=IndexCategoryGoodsBanner.objects.filter(category=category,display_type=0)[:4]
                    category.title = category_title
                    category_image=IndexCategoryGoodsBanner.objects.filter(category=category,display_type=1)[:4]
                    category.img = category_image
    
    
                context = {
                    "goods_cate":goods_cate,
                    "index_banners":index_banner,
                    "promotinon_banners":promotinon_banners,
                }
                # 把数据放到缓存中
                cache.set('index_page_data',context,3600)
    
            # 先把购物车的数量 设置为0
            cart_num = 0
            context.update(cart_num=0)
    
            return render(request,'index.html',context)
    View Code

    在redis中查询缓存的数据,第一次没有

    在浏览器中访问主页的视图:

    http://127.0.0.1:8000/index

    在redis中查询缓存的数据,会发现多了一条缓存数据

    设置有效期和admin管理类来维护缓存数据 

    1 在更新数据库的时候把缓存删掉  

    cash.delete("index_page_data")

    2 设置缓存的有效期

    cache.set("index_page_data", context, 3600)

    在admin中添加删除缓存的方法

    from django.core.cache import cache
    
    
    class BaseAdmin(admin.ModelAdmin):
        """admin站点的模型管理类,可以控制admin站点对于模型的展示修改等操作"""
        def save_model(self, request, obj, form, change):
            """admin站点在模型保存数据的时候调用"""
            # obj是要保存的模型对象(models里的类的对象)
            # 将数据保存到数据库中
            obj.save()
    
            # 调用生成静态页面的celery异步任务
            generate_static_index_html.delay()
    
            # 清除主页的缓存数据
            cache.delete("index_page_data")
    
        def delete_model(self, request, obj):
            """admin站点在模型删除数据的时候调用"""
            # 从数据库中删除
            obj.delete()
    
            # 调用生成静态页面的celery异步任务
            generate_static_index_html.delay()
    
            # 清除主页的缓存数据
            cache.delete("index_page_data")
    View Code

    判断用户是否是登陆的状态,如果是,则从购物车中获取数据:

    用户购物车在redis中存入的格式是哈希类型"cart_user_id":{"键":"值"},商品的id当成键,商品的数量当做值

    from django_redis import get_redis_connection 
    
    # 用户如果是登陆的情况从redis中获取购物车的数量
            if request.user.is_authenticated():
                redis_con = get_redis_connection("default")
                # cart是一个字典对象  {"sku_1":"1",'sku_2':'3' ---}
                cart = redis_con.hgetall('cart_%s' % request.user.id)
                # 遍历叠加
                for count in cart.values():
                    cart_num += int(count)
    
            context.update(cart_num=cart_num)

     把以上的代码添加到商品主页的视图IndexViews中:

    class IndexView(View):
        def get(self,request):
            # 后端需要想前端传送的数据有
            # 全部商品额分类信息
            # 轮播图的数据
            # 广告的信息
            # 分类商品展示的标题和图片
            # 用户购物车的信息
    
            # 先从缓存中取数据
            context=cache.get("index_page_data")
            if context is None:
                print("没有用上缓存的数据,从数据库中查询")
                # 获取所有的商品分类
                goods_cate=GoodsCategory.objects.all()
                # 获取轮播图信息
                index_banner = IndexGoodsBanner.objects.all().order_by("index")
                # 获取广告的信息
                promotinon_banners=IndexPromotionBanner.objects.all().order_by("index")
    
                for category in goods_cate:
                    # 主页分类商品的标题
                    category_title=IndexCategoryGoodsBanner.objects.filter(category=category,display_type=0)[:4]
                    category.title = category_title
                    category_image=IndexCategoryGoodsBanner.objects.filter(category=category,display_type=1)[:4]
                    category.img = category_image
    
    
                context = {
                    "goods_cate":goods_cate,
                    "index_banners":index_banner,
                    "promotinon_banners":promotinon_banners,
                }
                # 把数据放到缓存中
                cache.set('index_page_data',context,3600)
    
            # 先把购物车的数量 设置为0
            cart_num = 0
            # 用户如果是登陆的情况从redis中获取购物车的数量
            if request.user.is_authenticated():
                redis_con = get_redis_connection("default")
                # cart是一个字典对象  {"sku_1":"1",'sku_2':'3' ---}
                cart = redis_con.hgetall('cart_%s' % request.user.id)
                # 遍历叠加
                for count in cart.values():
                    cart_num += int(count)
    
            context.update(cart_num=cart_num)
    
            return render(request,'index.html',context)
    View Code

    商品详情页面视图

    商品详情页面的前端页面:

     

    根据前端的页面后端业务逻辑如下:

      1 查询全部商品分类的信息
      2 查询商品表的信息
      3 获取最新的推荐商品2个同类商品
      4 获取其他规格的商品
      5 从订单中获取评论信息
      6 购物车数量
      7 设置用户的浏览历史的记录

    class DetailView(View):
        def get(self, request,sku_id):
            # 查询全部商品分类的信息
            #  查询GoodSsku表的信息
            # 查询全部商品分类的信息
            goods_cate =GoodsCategory.objects.all()
            # 查询商品的信息
            try:
                sku = GoodsSKU.objects.get(id=sku_id)
            except GoodsSKU.DoesNotExist:
                # raise Http404('商品不存在')
                return redirect(reverse("goods:index"))
    
            # 获取最新的推荐商品2个同类商品
            new_goods = GoodsSKU.objects.filter(category=sku.category).order_by("-create_time")[:2]
            # 获取其他规格的商品
            goods_skus = sku.goods.goodssku_set.exclude(id=sku_id)
    
            # 从订单中获取评论信息
            sku_orders=sku.ordergoods_set.all().order_by("-create_time")[:30]
            if sku_orders:
                for sku_order in sku_orders:
                    sku_order.create_time = sku_order.create_time.strftime("%Y-%m-%d %H-%M-%S")
                    sku_order.username = sku_order.order.user.username
                else:
                    sku_orders=[]
    
            context = {
                "goods_cate": goods_cate,
                "sku": sku,
                "new_goods": new_goods,
                "goods_skus": goods_skus,
                "orders":sku_orders
            }
    
            # 购物车数量
            cart_num = 0
            # 如果是登录的用户
            if request.user.is_authenticated():
                user_id = request.user.id
                # 从redis中获取购物车信息
                redis_conn = get_redis_connection("default")
                # 如果redis中不存在,会返回0
                cart = redis_conn.hgetall("cart_%s" % user_id)
                for val in cart.values():
                    cart_num += int(val)
    
                # 浏览记录
                # 移除已经存在的本商品浏览记录
                redis_conn.lrem("history_%s" % user_id, 0, sku_id)
                # 添加新的浏览记录
                redis_conn.lpush("history_%s" % user_id, sku_id)
                # 只保存最多5条记录
                redis_conn.ltrim("history_%s" % user_id, 0, 4)
    
            context.update({"cart_num": cart_num})
    
    
    
            return render(request,'detail.html',context)
    View Code

    对上面的代码进行解析

    添加5条最新的浏览历史记录

    添加商品id的时候,先把购物车中包含本次的商品ID移除   (lrem("history_%s" % user_id, 0, sku_id))  0代表删除所有  

    再把现在的添加进去

    通过lrim只保存5条历史信息

    # 浏览记录
    # 移除已经存在的本商品浏览记录
    redis_conn.lrem("history_%s" % user_id, 0, sku_id)
    # 添加新的浏览记录
    redis_conn.lpush("history_%s" % user_id, sku_id)
    # 只保存最多5条记录
    redis_conn.ltrim("history_%s" % user_id, 0, 4)

    把所有能改成{%url "goods:detail" sku.id%}都改成这样的(商品详情页)

    可以随便点进一个详情页面,然后取uers/info中去查看历史记录有没有设置成功(获取redis中查看)

    为详情的页面设置缓存

    就添加几句代码

    class DetailView(View):
        def get(self, request,sku_id):
            # 查询全部商品分类的信息
            #  查询商品表的信息
            # 获取最新的推荐商品2个同类商品
            # 获取其他规格的商品
            # 从订单中获取评论信息
            # 购物车数量
            # 设置用户的浏览历史的记录
            context = cache.get("detail_data_%s" % requset.user.id)
            if context is None:
                goods_cate =GoodsCategory.objects.all()
                # 查询商品的信息
                try:
                    sku = GoodsSKU.objects.get(id=sku_id)
                except GoodsSKU.DoesNotExist:
                    # raise Http404('商品不存在')
                    return redirect(reverse("goods:index"))
    
                # 获取最新的推荐商品2个同类商品
                new_goods = GoodsSKU.objects.filter(category=sku.category).order_by("-create_time")[:2]
                # 获取其他规格的商品
                goods_skus = sku.goods.goodssku_set.exclude(id=sku_id)
    
                # 从订单中获取评论信息
                sku_orders=sku.ordergoods_set.all().order_by("-create_time")[:30]
                if sku_orders:
                    for sku_order in sku_orders:
                        sku_order.create_time = sku_order.create_time.strftime("%Y-%m-%d %H-%M-%S")
                        sku_order.username = sku_order.order.user.username
                    else:
                        sku_orders=[]
    
                context = {
                    "goods_cate": goods_cate,
                    "sku": sku,
                    "new_goods": new_goods,
                    "goods_skus": goods_skus,
                    "orders":sku_orders
                }
    
                # 设置缓存
                cache.set("detail_data_%s" % requset.user.id,context,3600)
    
            # 购物车数量
            cart_num = 0
            # 如果是登录的用户
            if request.user.is_authenticated():
                user_id = request.user.id
                # 从redis中获取购物车信息
                redis_conn = get_redis_connection("default")
                # 如果redis中不存在,会返回0
                cart = redis_conn.hgetall("cart_%s" % user_id)
                for val in cart.values():
                    cart_num += int(val)
    
                # 浏览记录
                # 移除已经存在的本商品浏览记录
                redis_conn.lrem("history_%s" % user_id, 0, sku_id)
                # 添加新的浏览记录
                redis_conn.lpush("history_%s" % user_id, sku_id)
                # 只保存最多5条记录
                redis_conn.ltrim("history_%s" % user_id, 0, 4)
    
            context.update({"cart_num": cart_num})
    
            return render(request,'detail.html',context)
    View Code

      

     
     
     
  • 相关阅读:
    [Swift通天遁地]三、手势与图表-(9)制作五彩缤纷的气泡图表
    hdu2289 Cup(二分)
    Makefile学习(三)[第二版]
    CABasicAnimation 基本动画
    iOS_20_微博自己定义可动画切换的导航控制器
    yispider 开源小说採集器 (来源http://git.oschina.net/yispider/yispider 我的改动版由于他的我无法跑)
    谈谈C++私有继承
    深入struts2.0(七)--ActionInvocation接口以及3DefaultActionInvocation类
    STL 之 list源码自行实现(iterator)
    二分lower_bound()与upper_bound()的运用
  • 原文地址:https://www.cnblogs.com/aaronthon/p/9347818.html
Copyright © 2020-2023  润新知