代码改变世界

购物车代码

2018-09-28 17:18  jinyanjun  阅读(1524)  评论(0编辑  收藏  举报

from django.shortcuts import render
from rest_framework.views import APIView
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework import status
import base64
import pickle

from . import serializers
from goods.models import SKU
# Create your views here.


class CartView(APIView):
"""购物车增,查,改,删(登录与未登录用户都需要实现以上四种逻辑)
用户登录才能访问该接口:前端程序员必须传入JWT token,后端需要指定权限
用户未登录也能访问该接口:前端程序员可以不传入JWT token,后端不需要指定权限

用户登录或者未登录都能够访问:需要传入JWT token,不能指定权限
考虑:前端程序员是否需要传入JWT token (需要传入JWT token,用于登录用户的验证)
考虑:后端是否需要指定权限 (不能指定,一旦指定权限,未登录用户就进不来)
"""

def perform_authentication(self, request):
"""为了保证在需要传入JWT token,不能指定权限的情况下,登录用户和未登录用户都可以进入到视图
如果不重写,不pass,那么未登录用户就会报401错误
类似于带不带工牌都可以进入到教室,再具体情况下,再去核对是否是本教室的学员
"""
pass

def post(self, request):
"""添加购物车"""
# 创建序列化器并校验,读取出校验之后的sku_id,count,selected
serializer = serializers.CartSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

sku_id = serializer.validated_data.get('sku_id')
count = serializer.validated_data.get('count')
selected = serializer.validated_data.get('selected')

# 判断用户是否登录:不管是否登录都可以继续执行,不能抛出异常
try:
user = request.user
except Exception:
user = None

if user is not None and user.is_authenticated:
# 如果用户已登录:将购物车数据存储到redis
# 创建连接到redis的对象
redis_conn = get_redis_connection('cart')

# 管道
pl = redis_conn.pipeline()

# 存储sku_id和count到hash
# hincrby : 会判断sku_id是否存在,如果存在就累加,反之,直接赋值
# redis_conn.hincrby('cart_%s' % user.id, sku_id, count)
pl.hincrby('cart_%s' % user.id, sku_id, count)

# 存储selected到set: 需要判断selected是否是True,只有为True时才要将上面的sku_id,添加到集合
if selected:
# redis_conn.sadd('selected_%s' % user.id, sku_id)
pl.sadd('selected_%s' % user.id, sku_id)

# 记得执行
pl.execute()

# 响应
return Response(serializer.data, status=status.HTTP_201_CREATED)

else:
# 如果用户未登录将购物车数据存储到cookie
# 读取cookie中的购物车数据
cookie_cart_str = request.COOKIES.get('cart')

# 判断用户是否使用cookie存储过购物车
if cookie_cart_str:
cookie_cart_str_bytes = cookie_cart_str.encode()
cookie_cart_dict_bytes = base64.b64decode(cookie_cart_str_bytes)
cookie_cart_dict = pickle.loads(cookie_cart_dict_bytes)
else:
cookie_cart_dict = {}

# 判断当前要存储的sku_id是否存在,如果存在就累加;反之直接新增
if sku_id in cookie_cart_dict:
origin_count = cookie_cart_dict[sku_id]['count']
count += origin_count

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

# 构造新的购物车编码字符串
new_cookie_cart_dict_bytes = pickle.dumps(cookie_cart_dict)
new_cookie_cart_str_bytes = base64.b64encode(new_cookie_cart_dict_bytes)
new_cookie_cart_str = new_cookie_cart_str_bytes.decode()

# 构造响应对象
response = Response(serializer.data, status=status.HTTP_201_CREATED)
# 在响应数据同时写入cookie
response.set_cookie('cart', new_cookie_cart_str)
# 响应
return response

def get(self, request):
"""获取购物车:不需要反序列化"""
# 判断用户是否登录:不管是否登录都可以继续执行,不能抛出异常
try:
user = request.user
except Exception:
user = None

if user is not None and user.is_authenticated:
# 如果用户已登录:将购物车数据存储到redis
# 创建连接到redis的对象
redis_conn = get_redis_connection('cart')
# 读取redis购物车中所有的sku_id和count(hash)
# {
# b'sku_id_1':b'count_1',
# b'sku_id_2': b'count_2'
# }
redis_cart_dict = redis_conn.hgetall('cart_%s' % user.id)

# 读物redis购物车中set里面所有的sku_id
# [sku_id_1]
redis_selected_set = redis_conn.smembers('selected_%s' % user.id)

# """
# {
# sku_id10: {
# "count": 10, // 数量
# "selected": True // 是否勾选
# },
# sku_id20: {
# "count": 20,
# "selected": False
# },
# ...
# }
# """

# 定义空字典,记录redis转换之后的购物车数据
cart_dict = {}
for sku_id, count in redis_cart_dict.items():
cart_dict[int(sku_id)] = {
"count": int(count),
"selected": sku_id in redis_selected_set # 如果sku_id在集合中,返回True;反之返回False
}

else:
# 如果用户未登录将购物车数据存储到cookie
# 读取cookie中的购物车数据
cookie_cart_str = request.COOKIES.get('cart')

# 判断用户是否使用cookie存储过购物车
if cookie_cart_str:
cookie_cart_str_bytes = cookie_cart_str.encode()
cookie_cart_dict_bytes = base64.b64decode(cookie_cart_str_bytes)
cart_dict = pickle.loads(cookie_cart_dict_bytes)
else:
cart_dict = {}

# 统一的查询,因为数据都整合完了
sku_ids = cart_dict.keys()
skus = SKU.objects.filter(id__in=sku_ids)

# 因为在响应时需要响应count和selected字段,但是sku里面没有,所以临时绑定一个
for sku in skus:
sku.count = cart_dict[sku.id]['count']
sku.selected = cart_dict[sku.id]['selected']

# 对skus进行列表级别的序列化器
serializer = serializers.CartSKUSerializer(skus, many=True)

return Response(serializer.data)

def put(self, request):
"""修改购物车"""
# 创建序列化器并校验,读取出校验之后的sku_id,count,selected
serializer = serializers.CartSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

sku_id = serializer.validated_data.get('sku_id')
count = serializer.validated_data.get('count')
selected = serializer.validated_data.get('selected')

# 判断用户是否登录:不管是否登录都可以继续执行,不能抛出异常
try:
user = request.user
except Exception:
user = None

if user is not None and user.is_authenticated:
# 如果用户已登录:将购物车数据存储到redis
# 创建连接到redis的对象
redis_conn = get_redis_connection('cart')

pl = redis_conn.pipeline()

# 修改redis中的hash里面的sku_id和count
# 由于前后端约定数据的传输是幂等的方式,前端直接给我们传递最终的结果,所以后端直接覆盖写入
pl.hset('cart_%s' % user.id, sku_id, count)

# 修改redis中的set里面的selected
if selected:
pl.sadd('selected_%s' % user.id, sku_id)
else:
pl.srem('selected_%s' % user.id, sku_id)

# 执行
pl.execute()

# 响应
return Response(serializer.data)
else:
# 如果用户未登录将购物车数据存储到cookie
# 读取cookie中的购物车数据
cookie_cart_str = request.COOKIES.get('cart')

# 判断用户是否使用cookie存储过购物车
if cookie_cart_str:
cookie_cart_str_bytes = cookie_cart_str.encode()
cookie_cart_dict_bytes = base64.b64decode(cookie_cart_str_bytes)
cart_dict = pickle.loads(cookie_cart_dict_bytes)
else:
cart_dict = {}

# 前后端约定是是幂等的设计方案,直接拿着用户最终的结果覆盖写入
cart_dict[sku_id] = {
'count':count,
'selected':selected
}

# 构造新的购物车编码字符串
new_cookie_cart_dict_bytes = pickle.dumps(cart_dict)
new_cookie_cart_str_bytes = base64.b64encode(new_cookie_cart_dict_bytes)
new_cookie_cart_str = new_cookie_cart_str_bytes.decode()

# 构造响应对象
response = Response(serializer.data)
# 在响应数据同时写入cookie
response.set_cookie('cart', new_cookie_cart_str)
# 响应
return response


def delete(self, request):
"""删除购物车"""
# 创建序列化器对象,校验sku_id, 读取校验之后的sku_id
serializer = serializers.CartDeleteSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

sku_id = serializer.validated_data.get('sku_id')

# 判断用户是否登录:不管是否登录都可以继续执行,不能抛出异常
try:
user = request.user
except Exception:
user = None

if user is not None and user.is_authenticated:
# 如果用户已登录:将购物车数据存储到redis
# 创建连接到redis的对象
redis_conn = get_redis_connection('cart')
# 管道
pl = redis_conn.pipeline()

# 删除hash里面的sku_id和count
pl.hdel('cart_%s' % user.id, sku_id)

# 删除set里面的sku_id
pl.srem('selected_%s' % user.id, sku_id)

# 记得execute
pl.execute()

return Response(status=status.HTTP_204_NO_CONTENT)
else:
# 如果用户未登录将购物车数据存储到cookie
# 读取cookie中的购物车数据
cookie_cart_str = request.COOKIES.get('cart')

# 判断用户是否使用cookie存储过购物车
if cookie_cart_str:
cookie_cart_str_bytes = cookie_cart_str.encode()
cookie_cart_dict_bytes = base64.b64decode(cookie_cart_str_bytes)
cart_dict = pickle.loads(cookie_cart_dict_bytes)
else:
cart_dict = {}

# 构造响应对象
response = Response(status=status.HTTP_204_NO_CONTENT)

# 删除字典里面的一条记录
if sku_id in cart_dict:
del cart_dict[sku_id]

# 构造新的购物车编码字符串
new_cookie_cart_dict_bytes = pickle.dumps(cart_dict)
new_cookie_cart_str_bytes = base64.b64encode(new_cookie_cart_dict_bytes)
new_cookie_cart_str = new_cookie_cart_str_bytes.decode()

# 在响应数据同时写入cookie
response.set_cookie('cart', new_cookie_cart_str)

# 响应
return response