分析与设计
删除购物车中的商品,需要接收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
测试:在购物车商品列表页,点击“删除”即可删除商品,总金额同步更新。