商品主页页面
商品主页页面的前端页面 效果图如下:
后端视图的业务逻辑处理:
根据前端展示的页面,后端需要向前端传送的数据有:
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)
前端模板商品分类要填充的代码如下:
<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 %}
可以在前端中判断用户是否登陆,如果登陆出现欢迎访问,没有则是登陆和注册
{% 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)
对以上的局部代码进行解释
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)
在后台管理页面把主页促销活动的盛夏的顺序改为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")
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")
判断用户是否是登陆的状态,如果是,则从购物车中获取数据:
用户购物车在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)