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

Posted 主打Python

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开源web框架django知识总结(十九)相关的知识,希望对你有一定的参考价值。

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

阿尔法商城(购物车)

购物车存储方案

新建apps->carts

  • 必须是用户登录状态下,才可以保存购物车数据。
  • 用户对购物车数据的操作包括:增、删、改、查、全选等等
  • 每个用户的购物车数据都要做唯一性的标识。

1. 购物车存储方案

1.存储数据说明

  • 如何描述一条完整的购物车记录?
    • 用户,选择了两个 iPhone8 添加到了购物车中,状态为勾选
  • 一条完整的购物车记录包括:用户商品数量勾选状态
  • 存储数据:user_id、sku_id、count、selected

2.存储位置说明

  • 购物车数据量小,结构简单,更新频繁,所以我们选择内存型数据库Redis进行存储。
  • 存储位置:dev.py文件中Redis数据库 5号库
"carts":  # 购物车
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.42.128:6379/5",
        "OPTIONS": 
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        
    ,

3.存储类型说明

  • 提示:我们很难将用户、商品、数量、勾选状态存放到一条Redis记录中。所以我们要把购物车数据合理的分开存储。
  • 用户、商品、数量:hash
    • carts_user_id: sku_id1: count, sku_id3: count, sku_id5: count, ...
  • 勾选状态:set
    • 只将已勾选商品的sku_id存储到set中,比如,1号和3号商品是被勾选的。
    • selected_user_id: [sku_id1, sku_id3, ...]
      注释:Redis Smembers 命令返回集合中的所有的成员。 不存在的集合 key 被视为空集合。

4.存储逻辑说明

  • 当要添加到购物车的商品已存在时,对商品数量进行累加计算。
  • 当要添加到购物车的商品不存在时,向hash中新增field和value即可。

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

购物车管理

添加购物车

提示:在商品详情页添加购物车使用局部刷新的效果。

1. 添加购物车接口设计和定义

1.请求方式

选项方案
请求方法post
请求地址/carts/

2.请求参数:JSON

参数名类型是否必传说明
sku_idint商品SKU编号
countint商品数量
selectedbool是否勾选

3.响应结果:JSON

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

2. 添加购物车后端逻辑实现

1.接收和校验参数 carts.views.py

import json

from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django_redis import get_redis_connection

from utils.views import login_required  # 注意修改导包路径
from goods.models import SKU
# Create your views here.

class CartsView(View):

 # 把sku商品加入购物车
    @method_decorator(login_required)
    def post(self, request):
        # 1、提取参数
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        selected = data.get('selected', True)

        # 2、校验参数
        if not all([sku_id, count]):
            return JsonResponse('code': 400, 'errmsg': '缺少参数!')

        if not isinstance(selected, bool):
            return JsonResponse('code': 400, 'errmsg': '参数有误!')

        # 3、判断是否登陆
        user = request.user
        if user.is_authenticated:
            # 4、登陆写入redis
            conn = get_redis_connection('carts')
            # 4.1 记录sku商品数量——carts_<user_id> : sku_id: count
            # conn.hmset('carts_%s'%user.id, sku_id:count) # 我们不能使用该函数,因为他会覆盖原有数据
            conn.hincrby('carts_%s'%user.id, sku_id, amount=count) # 把sku商品数量增加count,如果不存在则新建
            # 4.2 记录选中状态——selected_<user_id> : [sku_id]
            if selected:
                conn.sadd('selected_%s'%user.id, sku_id)

            return JsonResponse('code': 0, 'errmsg': 'ok')

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

展示购物车

1. 展示购物车接口设计和定义

1.请求方式

选项方案
请求方法GET
请求地址/carts/

**2.请求参数:**无

3.响应结果:HTML cart.html

4.后端接口定义 carts.views.py

class CartsView(View):

    def get(self, request):
        # 0、初始化一个空字典,用于保存sku购物车数据,其格式和cookie购物车格式一样
        cart_dict =  # 1: count:xx, selected:xx

        user = request.user
        if user.is_authenticated:
            # 1、用户登陆,则从redis中读取sku商品信息
           pass

2. 展示购物车后端逻辑实现

1.查询Redis购物车

2.查询购物车SKU信息

3.返回响应数据

 # 展示购物车
    @method_decorator(login_required)
    def get(self, request):
        # 0、初始化一个空字典,用于保存sku购物车数据,其格式和cookie购物车格式一样
        cart_dict =   # 1: count:xx, selected:xx

        user = request.user
        if user.is_authenticated:
            # 1、用户登陆,则从redis中读取sku商品信息
            conn = get_redis_connection('carts')
            # 2、sku商品数量、是否选中
            # cart_redis_dict = b'1': b'8'
            cart_redis_dict = conn.hgetall('carts_%s' % user.id)
            # cart_redis_selected = [b'1']
            cart_redis_selected = conn.smembers('selected_%s' % user.id) #smembers返回集合中的所有的成员。 不存在的集合 key 被视为空集合
            #将cart_redis_dict与cart_redis_selected合并
            for k, v in cart_redis_dict.items(): #字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
                # k: b'1'; v: b'8'
                cart_dict[int(k)] = 
                    'count': int(v),
                    'selected': k in cart_redis_selected  # b'1' in [b'1']返回True
                

        cart_skus = []
        # 3、构建响应返回
        for k, v in cart_dict.items():
            # k: sku_id; v: count:xx, selected: xx
            sku = SKU.objects.get(pk=k)
            cart_skus.append(
                'id': sku.id,
                'name': sku.name,
                'count': v['count'],
                'selected': v['selected'],
                'price': sku.price,
                'default_image_url': sku.default_image_url.url,
                'amount': sku.price * v['count']
            )

        return JsonResponse('code': 0, 'errmsg': 'ok', 'cart_skus': cart_skus)

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

修改购物车

提示:在购物车页面修改购物车使用局部刷新的效果。

1. 修改购物车接口设计和定义

1.请求方式

选项方案
请求方法PUT
请求地址/carts/

2.请求参数:JSON

参数名类型是否必传说明
sku_idint商品SKU编号
countint商品数量
selectedbool是否勾选

3.响应结果:JSON

字段说明
sku_id商品SKU编号
count商品数量
selected是否勾选

4.后端接口定义

# 修改购物车
    def put(self, request):

        user = request.user
        if user.is_authenticated:
            # 登陆,修改redis
          pass

2. 修改购物车后端逻辑实现

1.接收和校验参数

2.修改Redis购物车

# 修改购物车
    @method_decorator(login_required)
    def put(self, request):
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        selected = data.get('selected', True)

        user = request.user
        if user.is_authenticated:
            # 登陆,修改redis
            conn = get_redis_connection('carts')
            conn.hmset('carts_%s' % user.id, sku_id: count)  # 覆盖写入
            if selected:
                conn.sadd('selected_%s' % user.id, sku_id) #选中,sadd增加
            else:
                conn.srem('selected_%s' % user.id, sku_id) #未选中,srem删除
            return JsonResponse(
                'code': 0,
                'errmsg': 'ok',
                'cart_sku': 
                    'id': sku_id,
                    'count': count,
                    'selected': selected
                
            )

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

删除购物车

提示:在购物车页面删除购物车使用局部刷新的效果。

1. 删除购物车接口设计和定义

1.请求方式

选项方案
请求方法DELETE
请求地址/carts/

2.请求参数:JSON

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

3.响应结果:JSON

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

4.后端接口定义

# 删除购物车
    def delete(self, request):

        user = request.user
        # 1、已登陆
        if user.is_authenticated:
          pass

2. 删除购物车后端逻辑实现

1.接收和校验参数

2.删除Redis购物车

# 删除购物车
    @method_decorator(login_required)
    def delete(self, request):
        # "sku_id": xxx
        data = json.loads(
            request.body.decode()
        )
        sku_id = data.get('sku_id')

        user = request.user
        # 已登陆
        if user.is_authenticated:
            conn = get_redis_connection('carts')
            # 1.1 删除购物车哈希数据——carts_user_id :  sku_id : count
            # Hdel 命令用于删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略。
            conn.hdel('carts_%s' % user.id, sku_id)
            # 1.2 删除集合中的sku_id
            # Srem 命令用于移除集合中的一个或多个成员元素,不存在的成员元素会被忽略。
            #https://www.runoob.com/redis/redis-commands.html
            conn.srem('selected_%s' % user.id, sku_id)
            return JsonResponse('code': 0, 'errmsg': 'ok')

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

全选购物车

提示:在购物车页面修改购物车使用局部刷新的效果。

1. 全选购物车接口设计和定义

1.请求方式

选项方案
请求方法PUT
请求地址/carts/selection/

2.请求参数:JSON

参数名类型是否必传说明
selectedbool是否全选

3.响应结果:JSON

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

4.后端接口定义

class CartSelectAllView(View):

    # 设置全选/全取消
    def put(self, request):
        # 1、获取参数
        if user.is_authenticated:
            # 已登陆:把所有的sku_id加入/删除到selected_user_id的集合中
           pass

2. 全选购物车后端逻辑实现

1.接收和校验参数

2.全选Redis购物车

class CartSelectAllView(View):

    # 设置全选/全取消
    @method_decorator(login_required)
    def put(self, request):
        # 1、获取参数
        data = json.loads(request.body.decode())
        selected = data.get("selected") # True or False

        user = request.user
        if user.is_authenticated:
            # 已登陆:把所有的sku_id加入/删除到selected_user_id的集合中
            conn = get_redis_connection('carts')
            # 1、获取用户的购物车数据
            # b'1': b'5'
            cart_dict = conn.hgetall('carts_%s'%user.id)
            # [b'1', b'2']
            sku_ids = cart_dict.keys()
            # 2、设置全/全取消
            if selected:
                conn.sadd('selected_%s'%user.id, *sku_ids)
            else:
                conn.srem('selected_%s'%user.id, *sku_ids)
            return JsonResponse('code': 0, 'errmsg': 'ok')

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

展示商品页面简单购物车

需求:用户鼠标悬停在商品页面右上角购物车标签上,以下拉框形式展示当前购物车数据。

1. 简单购物车数据接口设计和定义

1.请求方式

选项方案
请求方法GET
请求地址/carts/simple/

**2.请求参数:**无

3.响应结果:JSON

字段说明
code状态码
errmsg错误信息
cart_skus[ ]简单购物车SKU列表
id购物车SKU编号
name购物车SKU名称
count购物车SKU数量
default_image_url购物车SKU图片

4.后端接口定义

class CartsSimpleView(View):
    """商品页面右上角购物车"""
 
    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录,查询Redis购物车
            pass
        # 构造简单购物车JSON数据
        pass

2. 简单购物车数据后端逻辑实现

1.查询Redis购物车

2.构造简单购物车JSON数据

class CartsSimpleView(View):

    def get(self, request):
        # 读取购物车数据,把具体的sku商品信息返回
        user = request.user

        # 1: "count":xx, "selected":xxx
        cart_dict =  # 购物车数据——格式和cookie格式是一样的

        if user.is_authenticated:
            # 已登陆,从redis中读商品数据
            conn = get_redis_connection('carts')
            # b'1': b'4'
            carts_redis = conn.hgetall('carts_%s'%user.id)
            # [b'1']
            carts_selected = conn.smembers('selected_%s'%user.id)
            for k,v in carts_redis.items():
                # k: b'1'; v: b'4'
                cart_dict[int(k)] = 
                    'count': int(v),
                    'selected': k in carts_selected
                


        # 构造响应数据
        cart_skus = []
        for k,v in cart_dict.items():
            # k: sku_id; v: "count":xx, "selected":xxx
            sku = SKU.objects.get(pk=k)
            cart_skus.append(
                'id': sku.id,
                'name': sku.name,
                'count': v['count'],
                'default_image_url': sku.default_image_url.url
            )

        return JsonResponse(
            'code': 0,
            'errmsg': 'ok',
            'cart_skus': cart_skus
        )

carts.urls.py

from django.urls import re_path
from .views import *

urlpatterns = [
    re_path(r'^carts/$', CartsView.as_view()),
    re_path(r'^carts/selection/$', CartSelectAllView.as_view()),
    re_path(r'^carts/simple/$', CartsSimpleView.as_view()),
]

修改前端文件

1、1.html 修改动态加载“简单购物车”显示

<!--      <div class="guest_cart fr">
            <a href="#" class="cart_name fl">我的购物车</a>
            <div class="goods_count fl" id="show_count">15</div>

            <ul class="cart_goods_show">
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>4</div>
                </li>
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>5</div>
                </li>
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>6</div>
                </li>
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>6</div>
                </li>
            </ul>
        </div>  -->
        
            <div class="guest_cart fr">
            <a href="#" class="cart_name fl">我的购物车</a>
            <div class="goods_count fl" id="show_count">[[ cart_total_count ]]</div>
            <ul class="cart_goods_show">
                <li v-for="sku in carts">
                    <img :src="sku.default_image_url" alt="商品图片">
                    <h4>[[ sku.name ]]</h4>
                    <div>[[ sku.count ]]</div>
                </li>
            </ul>
            </div>

修改

  • 2、修改detail.js

    改19行

    mounted: function()中添加:this.get_hot_goods();

    把注释部分打开

    var vm = new Vue(
        el: '#app',
        delimiters: ['[[', ']]'],
        data: 
            host,
            username: '',  //改
            user_id: sessionStorage.user_id || localStorage.user_id,
            token: sessionStorage.token || localStorage.token,
            tab_content: 
                detail: true,
                pack: false,
                comment: false,
                service: false
            ,
            sku_id: '',
            sku_count: 1,
            sku_price: price,
            cart_total_count: 0, // 购物车总数量
            carts: [], // 购物车数据
            hot_skus: [], // 热销商品
            cat: cat, // 商品类别
            comments: [], // 评论信息
            score_classes: 
                1: 'stars_one',
                2: 'stars_two',
                3: 'stars_three',
                4: 'stars_four',
                5: 'stars_five',
            
        ,
        computed: 
            sku_amount: function()
                return (this.sku_price * this.sku_count).toFixed(2);
            
        ,
        mounted: function()
            // 获取cookie中的用户名 (增)
        	this.username = getCookie('username');
            // 添加用户浏览历史记录
            this.get_sku_id();
    
            axios.post(this.host+'/browse_histories/', 
                sku_id: this.sku_id
            ,
                    responseType: 'json',
                    withCredentials:true,
                )
                .then(response=>
                    console.log(response)
    
                )
                .catch(error=>
                    console.log(error)
                )
    
            this.get_cart();
            this.get_hot_goods();
            this.get_comments();
        ,
        methods: 
             // 退出登录按钮
            logoutfunc: function () 
                var url = this.host + '/logout/';
                axios.delete(url, 
                    responseType: 'json',
                    withCredentials:true,
                )
                    .then(response => 
                        location.href = 'http://192.168.42.128/login.html';//改退出页面404错误
                    )
                    .catch(error => 
                        console.log(error.response);
                    )
            ,
            // 控制页面标签页展示
            on_tab_content: function(name)
                this.tab_content = 
                    detail: false,
                    pack: false,
                    comment: false,
                    service: false
                ;
                this.tab_content[name] = true;
            ,
            // 从路径中提取sku_id
            get_sku_id: function()
                var re = /^\\/goods\\/(\\d+).html$/;
                this.sku_id = document.location.pathname.match(re)[1];
            ,
            // 减小数值
            on_minus: function()
                if (this.sku_count > 1) 
                    this.sku_count--;
                
            ,
            // 获取用户所有的资料 (增:校验用户是否登陆状态)
            get

    以上是关于开源web框架django知识总结(十九)的主要内容,如果未能解决你的问题,请参考以下文章

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

    开源web框架django知识总结

    开源web框架django知识总结

    开源web框架django知识总结

    开源web框架django知识总结

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