05 商品模块

Posted aaronthon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

  

 

以上是关于05 商品模块的主要内容,如果未能解决你的问题,请参考以下文章

商品模块开发

如何有条件地将 C 代码片段编译到我的 Perl 模块?

启用 Proguard 后无法实例化片段

CTS测试CtsWindowManagerDeviceTestCases模块的testShowWhenLockedImeActivityAndShowSoftInput测试fail项解决方法(代码片段

完成后台管理系统功能添加商品中的商品类目的展示

ElasticSearch学习问题记录——Invalid shift value in prefixCoded bytes (is encoded value really an INT?)(代码片段