zhangyangcheng

商品主页页面

主要逻辑:

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

展示
  1 全部商品额分类信息
  2 轮播图的数据
  3 广告的信息
  4分类商品展示的标题和图片
  5用户购物车的信息

 

页面优化,可以采用两种方式:

1  页面静态化   

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

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

    把备份的数据(相同的数据)放到内存中,
    默认使用pickle将要缓存的数据转换成字符串,保存起来(我们的项目中redis)

缓存和静态化页面的区别:
获取首页时,静态化页面是用户未登陆的状态,购物车是写死的。
然而,缓存是把上次用户登陆的页面给缓存下来了。通过设置过期事件和admin站点增删修改数据,来清除缓存。让用户获得最新的数据

 

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

视图 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

 因为是在模板里渲染的,而静态文件实在staic 里面说以前端的index代码还要加入这一段:

{% load staticfiles %}
<!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="{% static "css/reset.css" %}">
    <link rel="stylesheet" type="text/css" href="{% static "css/main.css" %}">
    <script type="text/javascript" src="{% static "js/jquery-1.12.4.min.js" %}"></script>
    <script type="text/javascript" src="{% static "js/jquery-ui.min.js" %}"></script>
    <script type="text/javascript" src="{% static "js/slide.js" %}"></script>
</head>
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  然后开启分布式异步服务器:(前两个是开启分布式,后一个开启异步)
sudo service fdfs_trackerd start
sudo service fdfs_storaged start
sudo /usr/local/nginx/sbin/nginx

就会出现:

 

 页面静态化(重点)

把动态模板的页面渲染一次后,把结果保存下来,存成一个静态的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>
以前的如下所示:

 

 

 修改nginx的配置文件

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

 重启niginx服务器

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

开启celery服务器

cd      只写个cd就表示跳到家目录下  因为要把静态文件生成在家目录的项目下,所以celery的异步服务器也要开在那

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文件

当我们下次打开电脑的时候:也要在终端上开启或者重启nginx

sudo /usr/local/nginx/sbin/nginx -s reload  (重启     没有-s  reload 是开启)

 在浏览器中输入本机的ip地址,会调用静态化的文件    这两张ip地址(不用写端口号,因为已近默认写了)都会进入index的静态化页面

http://192.168.44.133 
http://127.0.0.1

而且在windows里也要可以输入  http://192.168.44.133/  (虚拟机的ip地址  )也可以访问到静态化页面,这里已经不是桌面的项目,而是home下项目的index.py文件()

 
 
这里还可以区别一下,我们怎么确定我们是访问的就是静态化页面?
把之前原本项目的goods/urls里的改为
 url(r"^index$", views.IndexView.as_view(), name="index"),

而之前的就是:
 url(r"^$", views.IndexView.as_view(), name="index"),

当我们在虚拟机的浏览器里输入:http://127.0.0.1:8000/index  访问的就不是静态化页面,而且在登陆的状态:会显示欢迎谁
当我们在虚拟机的浏览器里输入:http://127.0.0.1/   或者  http://192.168.44.133/ 访问的就是静态化页面:只会显示登陆  注册什么的
 

通过改写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")
这里在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

2 设置缓存的有效期
cache.set("index_page_data", context, 3600)

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

补充小的知识点,redis中保存了有:

购物车数据     哈希类型     \'cart_user.id\':{"sku_1":"1",\'sku_2\':\'3\' ---}

浏览记录  商品的id  列表类型   

session  用户的登陆状态

缓存  一段程序的输出结果

 

用户购物车在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)
View Code
 把以上的代码添加到商品主页的视图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)
View Code

把所有能改成{%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

 

分类:

技术点:

相关文章:

  • 2022-12-23
  • 2021-11-03
  • 2022-12-23
  • 2021-05-21
  • 2021-05-11
  • 2021-12-17
  • 2022-02-16
  • 2021-11-14
猜你喜欢
  • 2021-10-14
  • 2021-12-02
  • 2022-02-10
  • 2021-09-28
  • 2022-12-23
  • 2022-12-23
  • 2021-12-10
相关资源
相似解决方案