商品主页页面
主要逻辑:
根据前端展示的页面,后端需要向前端传送的数据有:
展示
1 全部商品额分类信息
2 轮播图的数据
3 广告的信息
4分类商品展示的标题和图片
5用户购物车的信息
页面优化,可以采用两种方式:
1 页面静态化
把动态模板的页面渲染一次后,把结果保存下来,存成一个静态的html文件,就是页面静态化,对于不经常变化的动态页面可以做静态化处理
把备份的数据(相同的数据)放到内存中,
默认使用pickle将要缓存的数据转换成字符串,保存起来(我们的项目中redis)
商品主页页面的前端页面 效果图如下:
视图 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)
前端模板商品分类要填充的代码如下:
<ul class="subnav fl"> {% for cate in goods_cate %} <li><a href="#model0{{ forloop.counter }}" class="{{ cate.logo }}">{{ cate.name }}</a></li> {% endfor %} </ul>
前端模板轮播图要填充的代码如下:
<ul class="slide_pics"> {% for slide in index_banners %} <li><a href="#"><img src="{{ slide.image.url }}" alt="幻灯片"></a></li> {% endfor %} </ul>
前端模板活动广告要填充的代码如下:
<div class="adv fl"> {% for add in promotinon_banners %} <a href="{{ add.url }}"><img src="{{ add.image.url }}"></a> {% endfor %} </div>
前端模板商品分类展示要填充的代码如下:
{% 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 %}
因为是在模板里渲染的,而静态文件实在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>
可以在前端中判断用户是否登陆,如果登陆出现欢迎访问,没有则是登陆和注册
{% 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)
对以上的局部代码进行解释
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 -s 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)
在后台管理页面把主页促销活动的盛夏的顺序改为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)
在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")
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)
把以上的代码添加到商品主页的视图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)
商品详情页面视图
商品详情页面的前端页面:
根据前端的页面后端业务逻辑如下:
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)
对上面的代码进行解析
添加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)