django购物车的实现
1 购物车的实现问题思路
购物车需求分析:
1 未登陆和已登陆都保存到用户的购物车数据。
2 用户可以对购物车进行增删改查;
3 购物车有选择状态,只有选中的状态才能生成订单;
4 用户登陆时,合并cookie
涉及到技术栈:
1 redis 的hash和set 的操作,增删改查及管道技术
2 cooike 的设置和删除以及解码和加码的
技术实现:
对于未登陆用户,购物车数据使用浏览器cookie保存
对于已登陆的用户,购物车在后端使redis保存,具体存储结果
hash {
使用幂等技术,多次点击完成后,得到最终的数量
数据库redis的设计:
hash: user_id sku_id count
set : sku_id (选中商品)
redis 中管道技术的实现:
1 创建管道实例
2 添加执行操作
3 管道统一执行
Cookie保存数据
涉及的知识点:
1 获取cookie中的card值
car_str=request.COOKIES.get(cart)
2 如果不为空,进行解码, 先进行二进制转换encode,然后base64.b64decode,然后再转换成字典
cart_dict=picker.loads(base64.b64decode(cart_str.encode()))
3 判断sku_id 存在 if sku_id in card_dict,
origin_count=card_dict[sku_id]['count']
count+=origin_count
4 进行更新数据
cart_dict[sku_id]={
'count':count,
'selected':selected
5 进行加密
card=picker.dumps(base64.b64encode(cart_dict.decode()))
6 保存cookie
response.set_cookie('card',card,24*3600 1
#有关cookie 解码和加密写单独函数进行实现
2 购物车的增加
2.1增加业务的思路
逻辑分析
# 1 前端发送增加请求,post请求,获取selected,sku_id ,count
# 2 首先检查商品
# 3 检查是用户是否登陆,
# 3.1 如何登陆,则存储到redis,
# 3.1.1 登陆Redis,
# 使用 redis hash user.id {sku_id:count}
# 使用 redis.con.hset('card_%s'%user.id, sku_id,count)
# 3.1.2 使用 hiscrby进行累计存储
redis.con.hincry('card_%s'%user.id,sku_id,count)
# 3.1.3 如何selected 选中,则,suk_id 存放到set
if selected:
redis.con.sadd('card_seleted_%s'%user.id .sku_id
# 3.2 没有登陆,存储cookie中
# 3.2.1获取cookie 的值
# 3.2.2 不存在,则创建 cart={},
# 3.2.3重新写入cooie 中,返回结果
# 4 返回结果
2.2 代码实现
def post(self,request):
# 1 前端发送增加请求,post请求,获取selected,sku_id ,count
dict_data=json.loads(request.body.decode())
sku_id=dict_data.get('sku_id')
count=dict_data.get('count')
selected=dict_data.get('selected')
# 2 首先检查商品
if not all([sku_id,count]):
return JsonResponse({'code':401,'errmsg':'传递的参数不全'})
try:
sku=SKU.objects.get(id=sku_id)
except SKU.DoesNotExist:
return JsonResponse({'code':402,'errmsg':'增加到购物车商品不存在'})
# 3 检查是用户是否登陆,
try:
user=request.user
except Exception:
user=None
# 3.1 如何登陆,则存储到redis,
# 3.1.1 登陆Redis,
if user is None and user.is_authenticated:
redis_con = get_redis_connection(CARD_CACHE_ALIAS)
rc_p = redis_con.pipeline()
# 购物车中无没有该商品,没有则增加,有则进行累计实现增量
if not rc_p.hexists('cart_%s'%user.id,sku_id):
rc_p.hset('cart_%s'% user.id,sku_id,count)
# 3.1.2使用hiscrby进行累计存 储
else:
rc_p.hincrby('cart_%s'%user.id, sku_id, count)
print(rc_p.hincrby('cart_%s'%user.id, sku_id, count))
# 3.1.3 如何select 选中,则,suk_id 存放到set
if selected:
rc_p.sadd('carts_selected_%s'%user.id,sku_id)
# 让管道集中处理
rc_p.execute()
return JsonResponse({'code': 200, 'errmsg': '完成添加'})
# 3.2 没有登陆,存储cookie中
else:
# 1获取cookie 的值
cart_dict=get_cookie_key(request,'cart')
# cart_str = request.COOKIES.get('cart')
# #2 判断是否为空,为空
# if cart_str is not None:
# cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
# 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
}
response=up_cookie_key('cart',cart_dict)
return response
# 4 返回结果
3 购物车的查询
3.1购物车查询的思路分析:
# 1 获取get请求,先判断是否为登陆客户
# 2 如果是登陆客户
# 连接redis
# 获取set的值
# 获取card_%对于的值
# 循环遍历获取sku_id
# 3 如果非登陆客户
# 获取cooke中cart的值card_dict
'''
3.2 代码实现
# 1 获取get请求先判断是否为登陆客户
try:
user=request.user
except:
user=None
# 2 如果是登陆客户
if user is not None and user.is_authenticated:
# 连接redis
# 获取card_%对于的
redis_con=get_redis_connection(CARD_CACHE_ALIAS)
# 获取set的值
cart_seleted=redis_con.smembers('cart_selected_%s'%user.id)
# 循环遍历获取sku_id
cart_redis=redis_con.hgetall('cart_%s'%user.id)
print(cart_redis)
print(type(cart_redis))
cart_data={}
for sku_id,count in cart_redis.items():
count=int(count.decode())
cart_data[sku_id.decode()]= {
'count':count,
'selected':sku_id in cart_seleted
}
# 3 如果非登陆客户
# 获取cooke中cart的值card_dict
else:
cart_data=get_cookie_key(request,'cart')
print(cart_data)
print(type(cart_data))
cart_list=[]
for sku_id in cart_data:
print(sku_id)
try:
sku=SKU.objects.get(id=sku_id)
except :
continue
cart_list.append({
'id': sku_id,
'name':sku.name,
'price':sku.price,
'count': cart_data[sku_id]['count'],
'selected':cart_data[sku_id]['selected']
})
return JsonResponse({'code':200,'data':cart_list})
4 购物车修改
4.1购物车修改的思路
# 1 获取请求参数
# 2 判断用户是否登陆
# 3 登陆,重置redis数量,(商品数量,默认选中)
# 4 未登陆 ,获取cookie card_data,修改数量
4.2 代码实现
def put(self,request):
# 1 获取请求参数
body_data = json.loads(request.body.decode())
sku_id = body_data.get('sku_id')
selected = body_data.get('selected')
count = body_data.get('count')
if sku_id is None:
return JsonResponse({'code': 401, 'errmsg': '参数不全'})
try:
sku = SKU.objects.get(id=sku_id)
except SKU.DoesNotExist:
return JsonResponse({'code': 401, 'errmsg': '商品错误'})
# 2 判断用户是否登陆
try:
user = request.user
except:
user = None
# 3 登陆,redis数量,(删除sku_id 相关 记录,如果选中,取消选中)
if user is not None and user.is_authenticated:
redis_con = get_redis_connection(CARD_CACHE_ALIAS)
redis_con.hset('card_%s'%user.id,sku_id,count)
if selected:
redis_con.sdd('card_selected_%s'%user.id,sku_id)
else:
redis_con.srem('card_selected_%s'%user.id,sku_id)
return JsonResponse({'code':200})
# 4 未登陆 ,获取cookie card_data,修改数量
else:
card_dict=get_cookie_key(request,'card')
origin_count=card_dict[sku_id]['count']
count+=origin_count
card_dict[sku_id]={
'count':count,
'selected':selected
}
response=up_cookie_key('card',card_dict)
return response
5 购物车删除
5.1购物车删除的思路
# 1 获取请求参数
# 2 判断用户是否登陆
# 3 登陆,redis数量,(删除sku_id 相关 记录,如果选中,取消选中)
# 4 未登陆 ,获取cookie card_data,删除相关记录
5.2 代码实现
def delete(self,request):
# 1 获取请求参数
body_data=json.loads(request.body.decode())
sku_id=body_data.get('sku_id')
if sku_id is None:
return JsonResponse({'code': 401, 'errmsg': '参数不全'})
try:
sku = SKU.objects.get(id=sku_id)
except SKU.DoesNotExist:
return JsonResponse({'code': 401, 'errmsg': '商品错误'})
# 2 判断用户是否登陆
try :
user=request.user
except :
user=None
# 3 登陆,redis数量,(删除sku_id 相关 记录,如果选中,取消选中)
if user is not None and user.is_authenticated:
redis_con=get_redis_connection(CARD_CACHE_ALIAS)
redis_con.hdel('cart_%s'%user.id,sku_id)
redis_con.srem('cart_select_%s'%user.id,sku_id)
return JsonResponse({'code':200})
else:
# 4 未登陆 ,获取cookie card_data,删除相关记录
cart_dict=get_cookie_key(request,'cart')
if sku_id in cart_dict:
del cart_dict[sku_id]
response=up_cookie_key('cart',cart_dict)
return response
6 购物车的合并问题
合并问题就是,cookie和redis的购物车商品记录的取舍问题,本思路采用,以cooike为准,如果,有则覆盖,没有则增加;
6.1 功能实现思路
合并cookie 购物车到redis方案有多种,本次采用用cookie为主,
cookie 数据 { reids 数据 {
'1': { hash '1': count ‘4’:count
'count': 1,
'selected':1} set { sku}
合并的的业务逻辑:
# 1 从cookie 读取cart的数据;返回是一个字典 cookie_dict,但是redis存储的二进制元素,需要进行单独decode
sku_list=[sku_id.decode() for sku_id in redis_data] 把字典的key 生成一个列表生成式
# 2 遍历cookie_dict
# 2.1打开 redis_con 直接向跟sku_id 向redis hash 进增加数据,有则修改,以cooike为主,没有则增加
# redis.con.hset('cart_%s'%user.id,sku_id, cookie[sku_id]['count'])
# 2.2判断 cookie[sku_id]['selected']为真,则 进行 redis_con.sadd('cart_selected_%s'%user.id,sku_id),
# 否则,判断 skui_id 是否在集合中,存在,则删除
# 3 删除cookie, 设置和删除cookie必须使用response
# response.delete_cookie('cart') ;退出的时候cart换成用户名即可
'''
6.2 功能实现代码
在系统登陆后直接调用该函数即可
from django.http import JsonResponse
from movice_fuli.settings import CARD_CACHE_ALIAS
def merege_cookie_redis(request,response):
# 1 从cookie 读取cart的数据;返回是一个字典 cookie_dict
card_dict = get_cookie_key(request, 'cart')
redis_con = get_redis_connection(CARD_CACHE_ALIAS)
user=request.user
redis_data=redis_con.hgetall('cart_%s'%user.id)
sku_list=[sku_id.decode() for sku_id in redis_data]
rp=redis_con.pipeline()
# 2 遍历cookie_dict
for sku_id in card_dict:
if sku_id in sku_list:
rp.hset('cart_%s'%user.id,sku_id,card_dict[sku_id]['count'])
if card_dict[sku_id]['selected']:
rp.sadd('cart_selected_%s'%user.id,sku_id)
else:
rp.srem('cart_selected_%s'%user.id,sku_id)
else:
rp.hset('cart_%s' % user.id, sku_id, card_dict[sku_id]['count'])
rp.execute()
# 3删除cookie信息
response.delete_cookie('cart')
return response