商品主页页面
商品主页页面的前端页面 效果图如下:
后端视图的业务逻辑处理:
根据前端展示的页面,后端需要向前端传送的数据有:
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"
把所有能改成{%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