开源web框架django知识总结(十八)

开源web框架django知识总结(十八)

商品详情页

商品详情页分析和准备

1. 商品详情页组成结构分析

1.商品频道分类

  • 封装在goods.utils.py文件中,直接调用方法即可。
from .models import GoodsCategory,GoodsChannel,SKUSpecification,SKU,SPUSpecification,SpecificationOption
from copy import deepcopy

def get_categories():
    # 模版参数categories是首页分类频道
    categories = {}

    # 获取首页所有的分类频道数据
    channels = GoodsChannel.objects.order_by(
        'group_id',
        'sequence'
    )
    # 遍历所有是分类频道,构建以组号作为key的键值对
    for channel in channels:
        # channel: GoodsChannel对象
        if channel.group_id not in categories:
            categories[channel.group_id] = {
                'channels': [],  # 当前分组中的分类频道(一级分类)
                'sub_cats': []  # 二级分类
            }
        # (1)、填充当前组中的一级分类
        cat1 = channel.category
        categories[channel.group_id]['channels'].append({
            'id': cat1.id,
            'name': cat1.name,
            'url': channel.url
        })

        # (2)、填充当前组中的二级分类
        cat2s = GoodsCategory.objects.filter(parent=cat1)
        for cat2 in cat2s:
            # cat2:二级分类对象

            cat3_list = []  # 每一次遍历到一个二级分类对象的时候,初始化一个空列表,用来构建三级分类
            cat3s = GoodsCategory.objects.filter(parent=cat2)
            # (3)、填充当前组中的三级分类
            for cat3 in cat3s:
                # cat3;三级分类对象
                cat3_list.append({
                    'id': cat3.id,
                    'name': cat3.name
                })

            categories[channel.group_id]['sub_cats'].append({
                'id': cat2.id,
                'name': cat2.name,
                'sub_cats': cat3_list  # 三级分类
            })

    return categories

2.面包屑导航

  • 已经提前封装在goods.utils.py文件中,直接调用方法即可。
def get_breadcrumb(category_id):
    # 根据category_id获取导航信息 这里category_id只是一个参数,并不是数据库字段名字
    ret_dict = {}

    category = GoodsCategory.objects.get(pk=category_id)
    # 1级
    if not category.parent:
        ret_dict['cat1'] = category.name
    # 2级
    elif not category.parent.parent:
        ret_dict['cat2'] = category.name
        ret_dict['cat1'] = category.parent.name
    # 3级
    elif not category.parent.parent.parent:
        ret_dict['cat3'] = category.name
        ret_dict['cat2'] = category.parent.name
        ret_dict['cat1'] = category.parent.parent.name

    return ret_dict

3.热销排行

  • 该接口已经在商品列表页中实现完毕,前端直接调用接口即可。

4.商品SKU信息(详情信息)

  • 通过sku_id可以找到SKU信息,然后渲染模板即可。
  • 使用Ajax实现局部刷新效果。

5.SKU规格信息

  • 通过SKU可以找到SPU规格和SKU规格信息。

6.商品详情介绍、规格与包装、售后服务

  • 通过SKU可以找到SPU信息,SPU中可以查询出商品详情介绍、规格与包装、售后服务。

  • 提前封装在goods.utils.py文件中,直接调用方法即可。

def get_goods_and_spec(sku_id):
    # 当前SKU商品
    sku = SKU.objects.get(pk=sku_id)
    # 记录当前sku的选项组合
    cur_sku_spec_options = SKUSpecification.objects.filter(sku=sku).order_by('spec_id')
    cur_sku_options = [] # [1,4,7]
    for temp in cur_sku_spec_options:
        # temp是SKUSpecification中间表对象
        cur_sku_options.append(temp.option_id)
    # spu对象(SPU商品)
    goods = sku.spu
    # 罗列出和当前sku同类的所有商品的选项和商品id的映射关系
    # {(1,4,7):1, (1,3,7):2}
    sku_options_mapping = {}
    skus = SKU.objects.filter(spu=goods)
    for temp_sku in skus:
        # temp_sku:每一个sku商品对象
        sku_spec_options = SKUSpecification.objects.filter(sku=temp_sku).order_by('spec_id')
        sku_options = []
        for temp in sku_spec_options:
            sku_options.append(temp.option_id) # [1,4,7]
        sku_options_mapping[tuple(sku_options)] = temp_sku.id # {(1,4,7):1}
    # specs当前页面需要渲染的所有规格
    specs = SPUSpecification.objects.filter(spu=goods).order_by('id')
    for index, spec in enumerate(specs):
        # spec每一个规格对象
        options = SpecificationOption.objects.filter(spec=spec)
        # 每一次选项规格的时候,准备一个当前sku的选项组合列表,便于后续使用
        temp_list = deepcopy(cur_sku_options) # [1,4,7]
        for option in options:
            # 每一个选项,动态添加一个sku_id值,来确定这个选项是否属于当前sku商品
            temp_list[index] = option.id # [1,3,7] --> sku_id?
            option.sku_id = sku_options_mapping.get(tuple(temp_list)) # 找到对应选项组合的sku_id
        # 在每一个规格对象中动态添加一个属性spec_options来记录当前规格有哪些选项
        spec.spec_options = options
    return goods, sku, specs

7.商品评价

  • 商品评价需要在生成了订单,对订单商品进行评价后再实现,商品评价信息是动态数据。

  • 使用Ajax实现局部刷新效果。
    开源web框架django知识总结(十八)
    开源web框架django知识总结(十八)

统计分类商品访问量

提示:

  • 统计分类商品访问量 是统计一天内该类别的商品被访问的次数。
  • 需要统计的数据,包括商品分类,访问次数,访问时间。
  • 一天内,一种类别,统计一条记录。
    开源web框架django知识总结(十八)

1. 统计分类商品访问量模型类

模型类定义在goods.models.py中,然后完成迁移建表。

class GoodsVisitCount(BaseModel):
    """统计分类商品访问量模型类"""
    category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='商品分类')
    count = models.IntegerField(verbose_name='访问量', default=0)
    date = models.DateField(auto_now_add=True, verbose_name='统计日期')

    class Meta:
        db_table = 'tb_goods_visit'
        verbose_name = '统计分类商品访问量'
        verbose_name_plural = verbose_name

用户浏览记录

设计浏览记录存储方案

  • 当登录用户在浏览商品的详情页时,我们就可以把详情页这件商品信息存储起来,作为该登录用户的浏览记录。
  • 用户未登录,我们不记录其商品浏览记录。

1. 存储数据说明

  • 虽然浏览记录界面上要展示商品的一些SKU信息,但是我们在存储时没有必要存很多SKU信息。
  • 我们选择存储SKU信息的唯一编号(sku_id)来表示该件商品的浏览记录。
  • 存储数据:sku_id

2. 存储位置说明

  • 用户浏览记录是临时数据,且经常变化,数据量不大,所以我们选择内存型数据库进行存储。
  • 存储位置:Redis数据库 4号库
"history": { # 用户浏览历史
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/4",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },

3. 存储类型说明

  • 由于用户浏览记录跟用户浏览商品详情的顺序有关,所以我们选择使用Redis中的list类型存储 sku_id
  • 每个用户维护一条浏览记录,且浏览记录都是独立存储的,不能共用。所以我们需要对用户的浏览记录进行唯一标识。
  • 我们可以使用登录用户的ID来唯一标识该用户的浏览记录。
  • 存储类型:‘history_user_id’ : [sku_id_1, sku_id_2, …]
    开源web框架django知识总结(十八)

4. 存储逻辑说明

  • SKU信息不能重复。
  • 最近一次浏览的商品SKU信息排在最前面,以此类推。
  • 每个用户的浏览记录最多存储五个商品SKU信息。
  • 存储逻辑:先去重,再存储,最后截取。

============================

保存和查询浏览记录

1. 保存用户浏览记录

1.请求方式

选项 方案
请求方法 POST
请求地址 /browse_histories/

2.请求参数:JSON

参数名 类型 是否必传 说明
sku_id string 商品SKU编号

3.响应结果:JSON

字段 说明
code 状态码
errmsg 错误信息

4.后端接口定义和实现users.views.py

from goods.models import SKU
class UserBrowseHistory(View):

    @method_decorator(login_required)
    def post(self, request):
        # 记录用户历史
        # 1、获取请求参数
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')

        user = request.user
        # 2、加入用户历史记录redis列表中
        conn = get_redis_connection('history')
        p = conn.pipeline()
        # 2.1 去重
        p.lrem('history_%s'%user.id, 0, sku_id)
        # 2.2 左侧插入
        p.lpush('history_%s'%user.id, sku_id)
        # 2.3 截断列表(保证最多5条)
        p.ltrim('history_%s'%user.id, 0, 4)
        p.execute()

        # 3、构建响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


    @method_decorator(login_required)
    def get(self, request):
        # 展示用户浏览历史
        user = request.user

        conn = get_redis_connection('history')
        # 1、读取redis浏览历史
        # sku_ids = [b'4', b'5', b'11'] 其中 0 表示列表的第一个元素以 -1 表示列表的最后一个元素
        sku_ids = conn.lrange('history_%s'%user.id, 0, -1)
        # 2、获取sku商品信息
        # sku_ids = [int(x) for x in sku_ids]
        skus = SKU.objects.filter(id__in=sku_ids)
        # 3、构建响应
        sku_list = []
        for sku in skus:
            sku_list.append({
                'id': sku.id,
                'name': sku.name,
                'price': sku.price,
                'default_image_url': sku.default_image_url.url
            })
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'skus': sku_list
        })

users.urls.py

# 记录用户历史
    re_path(r'^browse_histories/$', UserBrowseHistory.as_view()),

开源web框架django知识总结(十八)
开源web框架django知识总结(十八)

祝大家学习python顺利!

上一篇:django中:三大认证中认证、权限和频率的设置,自定制三大认证


下一篇:关于删除数据表之后,更改修改migrations记录的问题