1. 程式人生 > >關於REST風格API的設計2

關於REST風格API的設計2

from django.shortcuts import render

# Create your views here.
from django.shortcuts import render
from django_redis import get_redis_connection
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
import pickle
import base64

from goods.models import SKU
from . import constants
from .serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer, CartSelectAllSerializer


# Create your views here.


class CartView(GenericAPIView):
"""購物車"""
serializer_class = CartSerializer

# 身份驗證重寫留白
def perform_authentication(self, request):
"""將執行具體請求方法前的身份認證關掉,由檢視自己來進行身份認證"""
pass

def post(self, request):
# sku_id count selected
# 校驗
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# order_dict:validated_data
sku_id = serializer.validated_data['sku_id']
count = serializer.validated_data['count']
selected = serializer.validated_data['selected']

# 判斷使用者登入狀態
'''
使用者認證不成功,user方法會直接丟擲異常,所以身份認證需要進行錯誤捕捉
'''
try:
user = request.user # 匿名使用者 AnonymoseUser
except Exception:
user = None

#
if user and user.is_authenticated:
# 如果使用者已登入,儲存到redis
redis_conn = get_redis_connection('cart')
pl = redis_conn.pipeline()

# 使用者購物車資料 redis hash雜湊

'''
沒有就直接新增新的鍵值對,否則就直接追加資料
'''
pl.hincrby('cart_%s' % user.id, sku_id, count)

# 使用者購物車勾選資料 redis set
if selected:
pl.sadd('cart_selected_%s' % user.id, sku_id)

pl.execute()
return Response(serializer.data)

else:
# 如果使用者未登入,儲存到cookie reponse = Response() response.set_cookie
# 取出cookie中的購物車資料
cart_str = request.COOKIES.get('cart')

if cart_str: # 購物車不為空
# 解析
cart_str = cart_str.encode() # str -> bytes
cart_bytes = base64.b64decode(cart_str) # b64decode(byes型別)
cart_dict = pickle.loads(cart_bytes) # 字典型別
else:
cart_dict = {}

# cart_dict = {
# sku_id_1: {
# 'count': 10
# 'selected': True
# },
# sku_id_2: {
# 'count': 10
# 'selected': False
# },
# sku_id_3: {
# 'count': 10
# 'selected': True
# }
# }


if sku_id in cart_dict:
# 如果商品存在購物車中,累加
cart_dict[sku_id]['count'] += count
cart_dict[sku_id]['selected'] = selected
else:
# 如果商品不在購物車中,設定
cart_dict[sku_id] = {
'count': count,
'selected': selected
}

cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()

# 設定cookie
response = Response(serializer.data)
response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES)

return response

def get(self, request):
"""查詢購物車"""
"""
檢測使用者是否登入
"""
try:
user = request.user # user方法
except Exception:
user = None

# 查詢
if user and user.is_authenticated:
# 如果使用者已登入,從redis中查詢 sku_id count selected
redis_conn = get_redis_connection('cart')
redis_cart = redis_conn.hgetall('cart_%s' % user.id)
# redis_cart = {
# 商品的sku_id bytes位元組型別: 數量 bytes位元組型別
# 商品的sku_id bytes位元組型別: 數量 bytes位元組型別
# ...
# }
redis_cart_selected = redis_conn.smembers('cart_selected_%s' % user.id)
# redis_cart_selected = set(勾選的商品sku_id bytes位元組型別, ....)

# 遍歷 redis_cart,形成cart_dict
cart_dict = {}
for sku_id, count in redis_cart.items():
cart_dict[int(sku_id)] = {
'count': int(count),
'selected': sku_id in redis_cart_selected
}
else:
# 如果使用者未登入,從cookie中查詢
cookie_cart = request.COOKIES.get('cart')

if cookie_cart:
# 表示cookie中有購物車資料
# 解析
cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
else:
# 表示cookie中沒有購物車資料
cart_dict = {}

# cart_dict = {
# sku_id_1: {
# 'count': 10
# 'selected': True
# },
# sku_id_2: {
# 'count': 10
# 'selected': False
# },
# }

# 查詢資料庫
sku_id_list = cart_dict.keys()
sku_obj_list = SKU.objects.filter(id__in=sku_id_list)

# 遍歷sku_obj_list 向sku物件中新增count和selected屬性
for sku in sku_obj_list:
sku.count = cart_dict[sku.id]['count']
sku.selected = cart_dict[sku.id]['selected']

# 序列化返回
serializer = CartSKUSerializer(sku_obj_list, many=True)
return Response(serializer.data)


def put(self, request):
"""修改購物車"""
# sku_id, count, selected
# 校驗
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

sku_id = serializer.validated_data['sku_id']
count = serializer.validated_data['count']
selected = serializer.validated_data['selected']

# 判斷使用者的登入狀態
try:
user = request.user
except Exception:
user = None

# 儲存
if user and user.is_authenticated:
# 如果使用者已登入,修改redis
redis_conn = get_redis_connection('cart')
pl = redis_conn.pipeline()

# 處理數量 hash
pl.hset('cart_%s' % user.id, sku_id, count)

# 處理勾選狀態 set
if selected:
# 表示勾選
pl.sadd('cart_selected_%s' % user.id, sku_id)
else:
# 表示取消勾選, 刪除
pl.srem('cart_selected_%s' % user.id, sku_id)

pl.execute()
return Response(serializer.data)

else:
# 未登入,修改cookie
cookie_cart = request.COOKIES.get('cart')

if cookie_cart:
# 表示cookie中有購物車資料
# 解析
cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
else:
# 表示cookie中沒有購物車資料
cart_dict = {}
# cart_dict = {
# sku_id_1: {
# 'count': 10
# 'selected': True
# },
# sku_id_2: {
# 'count': 10
# 'selected': False
# },
# }

response = Response(serializer.data)

if sku_id in cart_dict:
cart_dict[sku_id] = {
'count': count,
'selected': selected
}

cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()

# 設定cookie
response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES)

return response

def delete(self, request):
"""刪除購物車資料"""
# 校驗資料: sku_id
serializer = CartDeleteSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
sku_id = serializer.validated_data['sku_id']

# 判斷使用者的登入狀態
try:
user = request.user
except Exception:
user = None

# 刪除
if user and user.is_authenticated:
# 已登入,刪除redis
redis_conn = get_redis_connection('cart')
pl = redis_conn.pipeline()
# 刪除hash
pl.hdel('cart_%s' % user.id, sku_id)
# 刪除set
pl.srem('cart_selected_%s' % user.id, sku_id)
pl.execute()
return Response(status=status.HTTP_204_NO_CONTENT)
else:
# 未登入,刪除cookie
cookie_cart = request.COOKIES.get('cart')

if cookie_cart:
# 表示cookie中有購物車資料
# 解析
cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
else:
# 表示cookie中沒有購物車資料
cart_dict = {}
# cart_dict = {
# sku_id_1: {
# 'count': 10
# 'selected': True
# },
# sku_id_2: {
# 'count': 10
# 'selected': False
# },
# }
response = Response(status=status.HTTP_204_NO_CONTENT)
if sku_id in cart_dict:
del cart_dict[sku_id]

cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()

# 設定cookie
response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES)

return response


class CartSelectAllView(GenericAPIView):
"""
購物車全選
"""
serializer_class = CartSelectAllSerializer

def perform_authentication(self, request):
pass

def put(self, request):
# selected
# 校驗
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
selected = serializer.validated_data['selected']

# 判斷使用者的登入狀態
try:
user = request.user
except Exception:
user = None

if user and user.is_authenticated:
# 已登入,redis
redis_conn = get_redis_connection('cart')
redis_cart = redis_conn.hgetall('cart_%s' % user.id)
# redis_cart = {
# 商品的sku_id bytes位元組型別: 數量 bytes位元組型別
# 商品的sku_id bytes位元組型別: 數量 bytes位元組型別
# ...
# }

sku_id_list = redis_cart.keys()
if selected:
# 全選, 所有的sku_id都新增到redis set
redis_conn.sadd('cart_selected_%s' % user.id, *sku_id_list)
else:
# 取消全選,清空redis中的set資料
redis_conn.srem('cart_selected_%s' % user.id, *sku_id_list)

return Response({'message': 'OK'})
else:
# 未登入, cookie
cookie_cart = request.COOKIES.get('cart')

if cookie_cart:
# 表示cookie中有購物車資料
# 解析
cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
else:
# 表示cookie中沒有購物車資料
cart_dict = {}
# cart_dict = {
# sku_id_1: {
# 'count': 10
# 'selected': True
# },
# sku_id_2: {
# 'count': 10
# 'selected': False
# },
# }

response = Response({'message': 'OK'})

if cart_dict:
for count_selected_dict in cart_dict.values():
count_selected_dict['selected'] = selected

cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()

# 設定cookie
response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES)

return response