17.2 修改购物车商品
17.2 修改购物车商品
问题分析
- 同样要分为登录和未登录两种状态分别处理。
- 要处理商品数据的变化及勾选状态的变化,还有商品总金额的变化 (在前端实现)。
- 若已登录,则需要覆盖redis中的数据。若未登录,则需要覆盖COOKIE中的数据。
设计
因只是对购物车中的商品数据进行修改,所以请求方式为put。
需要更新的数据为sku_id,count , selected几个参数。
实现
在carts应用下定义constants.py,存放常量
CARTS_COOKIE_EXPIRES = 3600 * 24 * 14 # 两周
carts应用下views.py中增加put方法,修改后的全部代码如下
import base64
import json
import picklefrom django.conf import settings
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connectionfrom carts import constants
from goods.models import SKU
from xiaoyu_mall_new.utils.response_code import RETCODEclass CartsView(View):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.userif 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 responsedef get(self, request):# 判断用户是否登录user = request.userif 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()# 一次性查询出所有的skusskus = 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_countcart_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