商品模块的其他页面
商品模块的详情页的功能:
页面信息的显示;购物车记录的添加;商品的其他规格展示
P62 商品详情信息的获取和显示
detail.html---其父模板为base_detail_list.html
定义对应的视图
class DetailView(View):
'''详情页'''
def get(self, request, goods_id):
'''显示详情页'''
return render(request, 'df_goods/detail.html')
当用户访问页面,点击之后会跳转到对应的详情页面,需要给详情页面传递参数sku_id标识点击的是哪一个商品,因此该页面的访问地址可以设计为/goods/商品id。此时路由可以设计为
re_path(r'^goods/(?P<goods_id>\d+)$', DetailView.as_view(), name='detail')
此时详情页应该进行接收goods_id,如上get方法所示。
详情页整体来说和首页一样主要是进行信息展示。此时分析该页面需要展示的信息即为视图函数中需要查询的信息。
获取商品信息的时候进行尝试查询try...except...
查询的时候有可能查询不到,进行except抛出异常
try:
sku = GoodsSKU.objects.get(id=goods_id)
except GoodsSKU.DoesNotExist:
# 商品不存在,直接跳转到首页。。。为了拉客户
return redirect(reverse("goods:index"))
# 获取商品的分类信息
types = GoodsType.objects.all()
# 获取商品的评论信息
# 根据表结构设计,评论信息在订单商品表中
# 根据skuid筛选订单商品信息,同时过滤掉其中为空的评论
sku_orders = OrderGoods.objects.filter(sku=sku).exclude(comment=" ")
# 获取新品推荐信息,按照创建时间进行降序排序,新品只取前两个
new_skus = GoodsSKU.objects.filter(type=sku.type).order_by('-create_time')[:2]
# 获取用户购物车中商品的数目
# 组织上下文context
context = {
'sku': sku,
'types': types,
'sku_orders': sku_orders,
'new_skus': new_skus,
'cart_count': cart_count
}
根据传过来的值修改相应的模板
捕获id,查询相应的数据,在页面上进行遍历显示
P61 用户历史浏览记录的添加
此时详情页面已经开发完毕,接下来访问首页中的链接应该跳转到详情页。故将首页中的链接进行修改【使用反向解析】
{% for banner in type.title_banners %}
{% url 'goods:detail' banner.sku.id %}
{% endfor %}
关于用户中心的历史浏览记录:
什么时候需要添加历史浏览记录?
访问商品的详情页面的时候(在商品详情对应的视图中),需要添加历史浏览记录
用户登录之后:1.获取用户购物车中商品的数目;2.添加用户的历史记录
添加历史浏览记录时,用户最新浏览的商品id从列表左侧插入
考虑如下情况:
用户浏览[3,2,1],之后用户又重新浏览2。此时的处理方案,先将2删除,随后又将2放到最左边。因此,访问时需要先看下列表中是否包含了当前访问数据,若有,则先将其移除,随后插入列表的最左侧。
移除命令
class redis.StrictRedis<---get_redis_connection返回该对象
lrem(name, count, value)----该函数的特点,当移除元素的时候,若该元素存在,则将其移除,否则什么都不做。因此不需要判断元素是否存在,直接进行移除操作即可
lpush(name, *values)---添加
ltrim(key, start, stop)---裁剪,只保留start和stop区间内的元素
# 获取用户购物车中商品的数目
user = request.user
cart_count = 0
if user.is_authenticated():
# 用户已登录
conn = get_redis_connection('default')
cart_key = 'cart_%d' %(user.id)
cart_count = conn.hlen(cart_key)
# 添加用户的历史记录
conn = get_redis_connection('default')
history_key = 'history_%d' %(user.id)
# 移除列表中的goods_id
conn.lrem(history_key, 0, goods_id)
# 把goods_id插入到列表的左侧
conn.lpush(history_key, goods_id)
# 只保存用户最新浏览的5条信息
conn.ltrim(history_key, 0, 4)
P63 获取同一SPU的其他规格的商品信息
在设计商品模块表格的时候,有SKU表还有SPU表【抽象出商品】
获取跟这个商品同一个种类的其他规格的商品
# 获取同一个SPU的其他规格商品(写在购物车前面)
依旧从具体商品表中进行查询,为了防止重复查询,去除自身
same_spu_skus = GoodsSKU.objects.filter(goods=sku.goods).exclude(id=goods_id)
放进上下文context,传给模板
在detail.html页面中的数量下方进行显示,进行遍历显示
P65 商品列表内容的获取和显示
1.修改list.html模板
2.
定义视图类
class ListView(View):
'''列表页'''
def get(self, request):
"""显示列表页"""
return render(request, 'list.html')
3.分析列表页面:
用户访问列表页面的时候,需要传给模板哪些信息
点种类的时候需要跳转到对应的列表页面
1.需要显示是哪个种类的---种类id,同一种类的都需要展示出来
2.要分页显示,需要告知是第几页---页码
3.排序方式
4.设计列表页地址:
# 设计时遵循restful api----》
restful在设计的时候,地址遵循一定的设计格式,用户在访问一个浏览器地址的时候,其实是在请求一种资源。
比如:goods/1实际上就是请求id为1的商品,属于restful api设计的一种风格
/list?type_id=种类id&page=页码&sort=排序方式
/list/种类id/页码/排序方式
/list/种类id/页码?sort=排序方式《----这里选择这一种
跟在?后面的参数需要通过request.GET来获取,其余的参数需要在URL中进行捕获
配置re_path(r'^list/(?P<type_id>\d+)/(?P<page>\d+)$', ListView.as_view(), name='list')
此时需要捕获其中的两个参数type_id和page
class ListView(View):
'''列表页'''
def get(self, request, type_id, page):
"""显示列表页"""
# 先获取种类信息,如果用户查询的种类信息不存在,则跳转至商品首页
try:
type = GoodsType.object.get(id=type_id)
except GoodsType.DoesNotExist:
# 种类不存在
return render(reverse("goods:index"))
# 获取商品的分类信息
types = GoodsType.objects.all()
# 获取排序的方式 获取分类s商品的信息
# sort=default 按照默认id排序
# sort=price 按照商品价格排序
# sort=hot 按照商品销量排序
sort = request.GET.get('sort')
if sort == 'price':
skus = GoodsSKU.objects.filter(type=type).order_by('price')
elif sort == 'hot':
skus = GoodsSKU.objects.filter(type=type).order_by('-sales')
else:
sort = 'default'
skus = GoodsSKU.objects.filter(type=type).order_by('-id')
# 对数据进行分页
#
return render(request, 'list.html')
此时分析列表页需要的数据:商品类型,分类商品展示,购物车信息,新品推荐
用户可以自己输入种类信息,这里进行参数校验,去数据库中查询是否有用户输入的数据信息
# 接下来获取分类商品的信息,从GoodsSKU表中查数据,此时商品需要按照某一信息进行排序,在查询之前需要获取排序方式
分页
Django文档中的Paginator类
from django.core.paginator import Paginator
class Paginator(object_list, per_page)
object_list:列表元组,查询集等可以遍历的数据
per_page:每页显示多少条数据
用户传过来的page:用户要获取第几页的内容,用户传过来的参数要进行容错。传递过来的参数捕获之后是字符串,这里尝试将其转换为数字。
try...except...
属性:
Paginator.num_pages:分页之后页面的总页数
Paginator.page_range:分页之后页码的列表
方法:
Paginator.page(number):返回Page类的实例对象,在实例对象页中包含了当前页的数据
Page类中
属性:
Page.object_list:当前页上所有对象的列表
Page.paginator: 相关的paginator对象
page.paginator.page_range---即可获取到分页之后页码的列表
Page.number---page对象的属性,当前页的序号
方法:
Page.has_next():如果有下一页,则返回True
Page.has_previous():如果有上一页,则返回True
####################################################
# 对数据进行分页, Paginator(object_list, per_page)
paginator = Paginator(skus, 1)
# 对页码进行容错处理:页码不是数字类型、页码不存在,此时页码一定在这个范围之内
# 获取第page页的内容
try:
page = int(page)
except Exception as e:
page = 1
if page > paginator.num_pages:
page = 1
# 获取第page页的Page实例对象
skus_page = paginator.page(page)
# todo: 进行页码的控制,页面上最多显示5个页码
# 新品推荐信息
new_skus = GoodsSKU.objects.filter(type=type).order_by('-create_time')[:2]
# 获取用户购物车中的商品数目
user = request.user
cart_count = 0
if user.is_authenicated():
# 用户已登录
conn = get_redis_connection('default')
cart_key = "cart_%d" %(user.id)
cart_count = conn.hlen(cart_key)
# 组织模板上下文
context = {
'type': type,
'types': types,
'new_skus': new_skus,
'cart_count': cart_count,
'sort': sort
}
# 将context传给模板
###############################################################
<div class="pagenation">
{# 有上一页 #}
{% if skus_page.has_previous %}
<a href="{% url 'goods:list' type.id skus_page.previous_page_number%}?sort={{ sort }}">上一页</a>
{% endif %}
{% for pageindex in skus_page.paginator.page_range %}
{% if pageindex == skus_page.number %}
<a href="{% url 'goods:list' type.id pageindex %}?sort={{ sort }}" class="active">{{ pageindex }}</a>
{% else %}
<a href="{% url 'goods:list' type.id pageindex %}?sort={{ sort }}">{{ pageindex }}</a>
{% endif %}
{% endfor %}
{# 有下一页 #}
{% if skus_page.has_next %}
<a href="{% url 'goods:list' type.id skus_page.next_page_number %}?sort={{ sort }}">下一页</a>
{% endif %}
</div>
# 在上一页或者下一页,要按照同样的排序方式显示,在传给模板数据的时候同样需要传入sort
当更换排序方式时,则将将其直接跳转到第一页
<div class="sort_bar">
<a href="{% url 'goods:list' type.id 1 %}" {% if sort == 'default' %}class="active"{% endif %}>默认</a>
<a href="{% url 'goods:list' type.id 1 %}?sort=price" {% if sort == 'price' %}class="active"{% endif %}>价格</a>
<a href="{% url 'goods:list' type.id 1 %}?sort=hot" {% if sort == 'hot' %}class="active"{% endif %}>人气</a>
</div>
P64 列表页页码控制
最多显示五个页码,当前页,当前页的前两页和当前页的后两页
此时不能在列表页上直接遍历分页之后的完整页码列表,需要自己在views中生成一个页码列表
# todo: 进行页码的控制,页面上最多显示5个页码
case1: 总页数小于5页,页面上显示所有页码
case2: 如果当前页是前三页,显示1-5页
case3: 如果当前页是后三页,显示后5页
case4: 其他情况,显示当前页的前2页,当前页,当前页的后2页
num_pages = paginator.num_pages
if num_pages < 5:
pages = range(1, num_pages + 1)
elif page < =3:
pages = range(1, 6)
elif num_pages - page <= 2:
pages = range(num_pages - 4, num_pages + 1)
else:
pages = range(page - 2, page + 3)
# 将页码列表 传给模板,在模板中遍历自己生成的页码列表pages