17.3 删除购物车商品

发布于:2025-08-16 ⋅ 阅读:(17) ⋅ 点赞:(0)

分析与设计

删除购物车中的商品,需要接收sku_id,以JSON来响应。

实现

在carts应用下视图类CartsView中增加delete方法,全部代码为

import base64
import json
import pickle

from django.conf import settings
from django.http import HttpResponseForbidden, JsonResponse, HttpRequest
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection

from carts import constants
from goods.models import SKU
from xiaoyu_mall_new.utils.response_code import RETCODE


class CartsView(View):

    def delete(self, request):
        """删除购物车商品"""
        # 接收并校验参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 判断商品是否存在
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseForbidden('商品不存在')

        user = request.user
        if user.is_authenticated:
            # 登录
            redis_conn = get_redis_connection('carts')

            pl = redis_conn.pipeline()
            pl.hdel('carts_%s' % user.id, sku_id)
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()
            return JsonResponse({'code': RETCODE.OK, 'errmsg': '删除购物车成功'})
        else:
            # 未登录
            carts_str = request.COOKIES.get('carts')

            if carts_str:
                cart_dict = pickle.loads(base64.b64decode(carts_str.encode()))

            else:
                cart_dict = {}
            # 响应对象
            response = JsonResponse({'code': RETCODE.OK, 'errmsg': '删除购物车成功'})

            if sku_id in cart_dict:
                del cart_dict[sku_id]
                cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('carts', cookie_cart_str, max_age=constants.CARTS_COOKIE_EXPIRES)

        return response

    def put(self, request):
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected')

        # 校验参数
        if not all([sku_id, count]):
            return HttpResponseForbidden('缺少必须的参数')

        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseForbidden('商品sku_id不存在')

        try:
            count = int(count)
        except Exception:
            return HttpResponseForbidden('参数count有误')

        if selected:
            if not isinstance(selected, bool):
                return HttpResponseForbidden('参数selected 有误')

        user = request.user
        if user.is_authenticated:
            # 登录状态
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hset('carts_%s' % user.id, sku_id)
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                pl.srem('selected_%s' % user.id, sku_id)

            pl.execute()

            # 创建响应
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'price': sku.price,
                'amount': sku.price * count,
                'stock': sku.stock,
                'default_image_url': settings.STATIC_URL + 'images/goods/' + sku.default_image.url + '.jpg',
            }
            return JsonResponse({'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})
        else:
            # 未登录 状态
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}

            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'price': sku.price,
                'amount': sku.price * count,
                'stock': sku.stock,
                'default_image_url': settings.STATIC_URL + 'images/goods/' + sku.default_image.url + '.jpg',
            }

            response = JsonResponse({'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})
            response.set_cookie('carts', cookie_cart_str, max_age=constants.CARTS_COOKIE_EXPIRES)
            return response

    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 创建连接到redis的对象
            redis_conn = get_redis_connection('carts')
            # 查询user_id、count与sku_id构成的购物车记录
            redis_cart = redis_conn.hgetall('carts_%s' % user.id)
            # 查询勾选的商品smembers 命令返回集合中的所有的成员
            redis_selected = redis_conn.smembers('selected_%s' % user.id)
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    "count": int(count),
                    "selected": sku_id in redis_selected
                }
        else:
            # 用户未登录,查询cookies购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 对 cart_str进行编码,获取字节类型的数据
                cart_str_bytes = cart_str.encode()
                # 对cart_str_bytes进行解码,获取明文数据
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                # 对cart_dict_bytes反序列化,转换成Python能识别的字典类型的数据
                cart_dict = pickle.loads(cart_dict_bytes)
            else:
                cart_dict = {}
        # 构造响应数据
        sku_ids = cart_dict.keys()
        # 一次性查询出所有的skus
        skus = SKU.objects.filter(id__in=sku_ids)
        cart_skus = []
        for sku in skus:
            cart_skus.append({
                'id': sku.id,
                'count': cart_dict.get(sku.id).get('count'),
                # 将True,转'True',方便json解析
                'selected': str(cart_dict.get(sku.id).get('selected')),
                'name': sku.name,
                'default_image_url': settings.STATIC_URL +
                                     'images/goods/' + sku.default_image.url + '.jpg',
                'price': str(sku.price),
                'amount': str(sku.price * cart_dict.get(sku.id).get('count')),
                'stock': sku.stock
            })
        context = {
            'cart_skus': cart_skus
        }
        # 渲染购物车页面
        return render(request, 'cart.html', context)

    def post(self, request):
        # 将JSON格式的字符串反序列化为Python对象
        json_dict = json.loads(request.body.decode())
        # 接收参数
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)

        # 校验参数
        if not all([sku_id, count]):
            return HttpResponseForbidden('缺少必要参数')

        # 校验sku_id参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseForbidden('参数sku_id错误')
        # 校验count参数
        try:
            count = int(count)
        except Exception:
            return HttpResponseForbidden('参数count错误')

        # 校验selected参数
        if selected:
            if not isinstance(selected, bool):
                return HttpResponseForbidden('参数selected错误')

        # 判断用户是否登录
        user = request.user
        # 已登录,数据格式为:
        # carts_user_id:{sku_id1:count1,sku_id2:count2,...}
        # selected_user_id:{sku_id1,sku_id2,...}
        if user.is_authenticated:
            redis_conn = get_redis_connection('carts')
            # 创建管道,用于执行多个命令
            pl = redis_conn.pipeline()
            # 以增量形式保存商品数据
            # - 'carts_%s' % user.id :生成以用户ID为后缀的购物车键名(如 carts_123 )
            # - sku_id :商品SKU的唯一标识,作为哈希表的字段名
            # - count :要增加的数量(可为正数或负数)
            pl.hincrby('carts_%s' % user.id, sku_id, count)
            # 保存商品的勾选状态
            if selected:
                # 若selected为True,将sku_id添加到selected集合中
                pl.sadd('selected_%s' % user.id, sku_id)

            # 执行管道命令
            pl.execute()
            return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:  # 未登录,购物车数据存储在cookie中,数据结构如下
            # cart_dict = {
            #     'sku_id1': {'count': 5, 'selected': 'True'},
            #     'sku_id2': {'count': 3, 'selected': 'False'}
            # ...
            # }
            cart_str = request.COOKIES.get('carts')
            # 若购物车数据存在,将其反序列化为字典
            if cart_str:
                # 对cart_str进行编码,获取字节类型数据
                cart_str_bytes = cart_str.encode()
                # 对密文类型数据cart_str_bytes进行base64解码,获取明文数据
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                # 对明文类型数据cart_dict_bytes进行反序列化,获取字典类型数据
                cart_dict = pickle.loads(cart_dict_bytes)
            # 若没有数据,创建空字典
            else:
                cart_dict = {}
            # 若购物车数据中已存在该商品,累加数量
            if sku_id in cart_dict:
                origin_count = cart_dict[sku_id]['count']
                count += origin_count
            cart_dict[sku_id] = {'count': count, 'selected': selected}
            # 对字典类型数据cart_dict进行序列化,获取字节类型数据
            cart_dict_bytes = pickle.dumps(cart_dict)
            # 对字节类型数据cart_dict_bytes进行base64编码,获取密文类型数据
            cart_str_bytes = base64.b64encode(cart_dict_bytes)
            # 对密文类型数据cart_str_bytes进行解码,获取明文类型数据
            cookie_cart_str = cart_str_bytes.decode()

            response = JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            response.set_cookie('carts', cookie_cart_str)
            # 响应结果
            return response

测试:在购物车商品列表页,点击“删除”即可删除商品,总金额同步更新。


网站公告

今日签到

点亮在社区的每一天
去签到