本文共 13732 字,大约阅读时间需要 45 分钟。
存储数据说明
如何描述一条完整的购物车记录? 用户itcast,选择了两个 iPhone8 添加到了购物车中,状态为勾选 一条完整的购物车记录包括:用户、商品、数量、勾选状态。 存储数据: user_id、sku_id、count、selected
存储位置说明
购物车数据量小,结构简单,更新频繁,所以我们选择内存型数据库Redis进行存储。 存储位置:Redis数据库
Hash 字典格式 cart_USER_ID {sku_id:count} 商品id和数量set 集合 selected_USER_ID [sku_id] 是否被选中
把数据添加到cookie 登录之后再存入redis
cookie数据格式 json字符串
cart = { sku_id:{count:数量,selected:布尔值 是否选中}, xxx: {xxx: xxx, xxx:xxx}}
流程:
1. 获取参数 商品的id 和 数量 2. 校验参数 3. 判断用户是否登录 3.1 如果登录 3.1.1 连接redis 数据存入redis 3.2 没有登录 3.2.1 取出cookie 3.2.2 如果有cookie就解码 3.2.3 没有cookie就创建新的字典来存放数据 方便存入cookie 3.2.4 如果商品已经存在购物车再添加就 已有数量+=添加数量 3.2.5 组织数据存入字典 3.2.6 稍微加密存入cookie
class CartsView(View): def post(self, request): """ 购物车添加 :param request: :return: """ # 1. 获取参数 商品id 和 商品数量 json_data = json.loads(request.body) sku_id = json_data.get('sku_id') count = json_data.get('count') # 校验参数 是否存在该商品 try: SKU.objects.get(id=sku_id) except Exception as e: print(e) return JsonResponse({ 'code': 400, 'errmsg': '商品不存在'}) # 数量是否是数字 try: count = int(count) except Exception as e: print(e) return JsonResponse({ 'code': 400, 'errmsg': '参数错误'}) # 判断用户是否登录 user = request.user # 如果登录存入redis if user.is_authenticated: # 连接redis redis_conn = get_redis_connection('carts') # 存入hash 字典 cart_userId {sku_id:count} redis_conn.hset('cart_%s' % user.id, sku_id, count) # 存入set集合 是否选中 selected_userId [sku_id1,sku_id2,sku_id3] redis_conn.sadd("selected_%s" % user.id, sku_id) return JsonResponse({ 'code': 0, 'errmsg': 'ok'}) # 没有登录购物车数据存入cookie else: import base64 import pickle # 先获取cookie cart_cookie = request.COOKIES.get('cart') # 如果有cookie就解码 base64解码 pickle二进制数据转换 if cart_cookie: carts = pickle.loads(base64.b64decode(cart_cookie)) # 没有就创建新字典 else: carts = { } # 如果商品已经存在购物车 就把数量+1 if sku_id in carts: count_ori = carts[sku_id]['count'] count += count_ori # 组织数据存入cookie carts[sku_id] = { 'count': count, 'selected': True } # 稍微加密一下 carts_bytes = pickle.dumps(carts) b64carts = base64.b64encode(carts_bytes) # 存入cookie response = JsonResponse({ 'code': 0, 'errmsg': 'ok'}) response.set_cookie('cart', b64carts.decode(), max_age=3600 * 24 * 10) return response
流程
1. get请求查看购物车 判断用户是否登录 如果登录: 1.1 连接redis 取出购物车和是否被选中的数据 1.2 组织成字典数据 carts = {数据} 没有登录: 1.1取出cookie 1.2 如果有cookie就解码 没有cookie就创建一个空字典数据 carts={数据} 2. 取出购物车数据内所有的key 为sku_id的列表 3. 查询数据库内所有商品 根据id查询出在购物车列表内的数据 4. 组织数据返回给前端
代码
class CartsView(View): def get(self, request): # 判断用户是否登录 user = request.user if user.is_authenticated: # 如果登录连接redis redis_conn = get_redis_connection('carts') # 取出redis内的所有购物车数据 sku_id_count = redis_conn.hgetall('cart_%s' % user.id) # 取出所有选中的列表 selected_sku_ids = redis_conn.smembers('selected_%s' % user.id) # 组织数据 carts = { } # redis取出是二进制数据 要强转一下 for sku_id, count in sku_id_count.items(): carts[int(sku_id)] = { 'count': int(count), 'selected': sku_id in selected_sku_ids } else: # 没有登录取出cookie cart_cookie = request.COOKIES.get('cart') # 有cookie 就解码 没有cookie就创建一个空字典 if cart_cookie: carts = pickle.loads(base64.b64decode(cart_cookie)) else: carts = { } # 取出数据中所有的key 为sku_id sku_ids = carts.keys() # 查询所有商品 返回出id在购物车列表内的 skus = SKU.objects.filter(id__in=sku_ids) # 组织数据 id name price 图片 数量 是否被选中 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, 'count': carts[sku.id]['count'], 'selected': carts[sku.id]['selected'], }) # 返回数据 return JsonResponse({ 'code': 0, 'errmsg': 'ok', 'cart_skus': sku_list})
页面
流程
1. 获取前端参数 商品id 数量 和 是否被选中 2. 校验参数 3. 判断用户是否登录 登录: 3.1 连接redis 3.2 修改数据 3.3 组织返回前端数据 没有登录: 3.1 取出cookie 3.2 解码 3.3 修改数据 3.4 加密 3.5 组织返回前端的数据 3.6 返回存入cookie
代码
class CartsView(View): def put(self, request): # 获取参数 数量 是否选中 和 商品id json_data = json.loads(request.body) count = json_data.get('count') selected = json_data.get('selected') sku_id = json_data.get('sku_id') # 校验参数 商品是否真实存在 数量是否是数字 selected是否是布尔值 try: SKU.objects.get(id=sku_id) except Exception as e: print(e) return HttpResponseBadRequest('商品不存在') try: count = int(count) except Exception as e: print(e) return HttpResponseBadRequest('参数错误') if not isinstance(selected, bool): return HttpResponseBadRequest('商品不存在') # 判断用户是否登录 user = request.user if user.is_authenticated: # 登录连接redis redis_conn = get_redis_connection('carts') # 修改数据 redis_conn.hset('cart_%s' % user.id, sku_id, count) # 修改是否被选中 if selected: redis_conn.sadd('selected_%s' % user.id, sku_id) else: redis_conn.srem('selected_%s' % user.id, sku_id) # 组织返回前端的数据 cart_sku = { 'count': count, 'selected': selected, 'sku_id': sku_id, } return JsonResponse({ 'code': 0, 'errmsg': 'ok', 'cart_sku': cart_sku}) else: # 没有登录 取出cookie cart_cookie = request.COOKIES.get('cart') # 解码 carts = pickle.loads(base64.b64decode(cart_cookie)) # 修改数据 carts[sku_id]['count'] = count carts[sku_id]['selected'] = selected # 稍微加密 carts_bytes = pickle.dumps(carts) b64carts = base64.b64encode(carts_bytes) # 组织返回前端的数据 cart_sku = { 'count': count, 'selected': selected, 'sku_id': sku_id, } # 添加到cookie response = JsonResponse({ 'code': 0, 'errmsg': 'ok', 'cart_sku': cart_sku}) response.set_cookie('cart', b64carts.decode(), max_age=3600 * 24 * 10) return response
流程:
1. 获取参数 2. 校验参数 3. 判断用户是否登录 登录: 3.1 连接redis 3.2 删除数据 3.3 返回响应 没有登录: 3.1 获取cookie 3.2 解码 3.3 在cookie字典内删除用户要删除的商品id 3.4 设置cookie 3.5 返回响应
代码
class CartsView(View): def delete(self, request): # 获取参数 要删除购物车内哪一个商品 json_data = json.loads(request.body) sku_id = json_data.get('sku_id') # 校验参数 try: SKU.objects.get(id=sku_id) except Exception as e: print(e) return HttpResponseBadRequest('商品不存在') # 判断用户是否登录 user = request.user if user.is_authenticated: # 如果登录 连接redis redis_conn = get_redis_connection('carts') # 删除数据 redis_conn.hdel('cart_%s' % user.id, sku_id) redis_conn.srem('selected_%s' % user.id, sku_id) # 返回响应 return JsonResponse({ 'code': 0, 'errmsg': 'ok'}) else: # 没有登录 获取cookie cart_cookie = request.COOKIES.get('cart') # 解码 carts = pickle.loads(base64.b64decode(cart_cookie)) # 删除cookie内的该商品 del carts[sku_id] # 加密 carts_bytes = pickle.dumps(carts) base64_carts = base64.b64encode(carts_bytes) # 设置cookie 返回响应 response = JsonResponse({ 'code': 0, 'errmsg': 'ok'}) response.set_cookie('cart', base64_carts.decode(), max_age=3600 * 24 * 10) return response
页面
流程
1. 获取参数 selected 布尔值 True为勾选 False为不勾选 2. 校验参数 3. 判断用户是否登录 登录: 3.1 连接redis 3.2 取出购物车数据 获取到购物车内所有的商品id 3.3 把所有的商品id添加到存放是否选中的set集合中(自动去重) 3.4 返回响应 没有登录: 3.1 取出cookie 3.2 解码 3.3 如果勾选修改selected都为True 反之则设置为False 3.4 加密 3.5 设置cookie 3.6 返回响应
代码
class CartsSelect(View): def put(self, request): # 获取参数 selected全选按钮是否勾选 布尔值 json_data = json.loads(request.body) selected = json_data.get('selected') # 校验参数 if not isinstance(selected, bool): return HttpResponseBadRequest('商品不存在') # 判断用户是否登录 user = request.user if user.is_authenticated: # 如果登录 连接redis redis_conn = get_redis_connection('carts') # 获取到所有购物车数据 cart_list = redis_conn.hgetall('cart_%s' % user.id) # 取出key key为商品id cart_key_list = cart_list.keys() # 遍历商品id for key in cart_key_list: # 如果全选选中 就把所有商品id添加到set集合 不用去重 因为set集合自动去重 if selected: redis_conn.sadd('selected_%s' % user.id, key) # 如果没有勾选 就把set集合内所有商品id删除 else: redis_conn.srem('selected_%s' % user.id, key) return JsonResponse({ 'code': 0, 'errmsg': 'ok'}) else: # 没有登录就获取cookie cart_cookie = request.COOKIES.get('cart') # 解码 carts = pickle.loads(base64.b64decode(cart_cookie)) # 遍历字典 拆包 k为sku_id v 为 {count:xxx,selected:bool} for k, v in carts.items(): # 如果选中 就把所有的sku_id的selected设置为True if selected: v['selected'] = True # 没有选中则设置为False else: v['selected'] = False # 加密 b64carts = base64.b64encode(pickle.dumps(carts)) # 设置cookie 返回响应 response = JsonResponse({ 'code': 0, 'errmsg': 'ok'}) response.set_cookie('cart', b64carts.decode(), max_age=3600 * 24 * 10) return response
页面
鼠标移动到购物车显示数据
前端需要的数据格式
请求方式
流程1. 获取用户对象判断有没有登录2. 登录了: 查询redis中的购物车数据 没有登录: 查询cookie中的购物车数据3. 取出商品数据添加到列表返回
代码
class CartsSimpleView(View): def get(self, request): # 获取用户对象 user = request.user # 组织数据的列表 cart_skus = [] # 如果用户存在就去连接redis取出购物车数据 if user.is_authenticated: redis_conn = get_redis_connection('carts') cart_data = redis_conn.hgetall('cart_%s' % user.id) sku_id_list = cart_data.keys() for sku_id in sku_id_list: count = int(cart_data.get(sku_id)) sku = SKU.objects.get(id=int(sku_id)) # 组织数据添加到列表 cart_skus.append({ 'id': sku.id, 'name': sku.name, 'count': count, 'default_image_url': sku.default_image.url, }) return JsonResponse({ 'code': 0, 'errmsg': 'ok', 'cart_skus': cart_skus}) else: # 如果用户不存在就去cookie中取出购物车数据 cart_cookie = request.COOKIES.get('cart') # 解码 cart_cookie_data = pickle.loads(base64.b64decode(cart_cookie)) # 遍历商品id sku_id_list = cart_cookie_data.keys() for sku_id in sku_id_list: count = cart_cookie_data[sku_id]['count'] sku = SKU.objects.get(id=int(sku_id)) # 组织数据添加到列表 cart_skus.append({ 'id': sku.id, 'name': sku.name, 'count': count, 'default_image_url': sku.default_image.url, }) return JsonResponse({ 'code': 0, 'errmsg': 'ok', 'cart_skus': cart_skus})
在用户未登录的时候购物车数据存在cookie中,用户登录之后把cookie中的数据合并到redis中
在QQ登录、登录、注册之后自动登录的时候调用合并的方法
代码
def merge_cart_cookie_to_redis(request, user, response): """ 用户登录合并cookie中的购物车数据到redis :param request: 请求体 :param user: 用户对象 :param response: 响应体 :return: 返回响应体 """ # 取出cookie中的购物车数据 cart_cookie = request.COOKIES.get('cart') # 解码 cookie_cart_dict = pickle.loads(base64.b64decode(cart_cookie)) # 如果没有cookie直接返回响应 if not cookie_cart_dict: return response # 组织新字典来存放购物车数据 new_cart_dict = { } # 被选中的商品id列表 new_cart_selected_add = [] # 没有被选中的商品id列表 new_cart_selected_rm = [] # 遍历cookie购物车数据存放到新字典 for sku_id, count_sel in cookie_cart_dict.items(): new_cart_dict[sku_id] = count_sel['count'] # 如果选中了就添加到add列表 没有被选中就添加到rm列表 if count_sel['selected']: new_cart_selected_add.append(sku_id) else: new_cart_selected_rm.append(sku_id) # 连接redis redis_conn = get_redis_connection('carts') # 创建管道 pl = redis_conn.pipeline() # 把cookie中的购物车数据添加到redis中 pl.hmset('cart_%s' % user.id, new_cart_dict) # 选中数据中添加被选中的商品 if new_cart_selected_add: pl.sadd('selected_%s' % user.id, *new_cart_selected_add) # 选中数据中删除没有被选中的商品 if new_cart_selected_rm: pl.srem('selected_%s' % user.id, *new_cart_selected_rm) # 提交操作 pl.execute() # 删除cookie中的购物车数据 response.delete_cookie('cart') # 返回响应 return response
转载地址:http://nquhf.baihongyu.com/