1. 程式人生 > >添加到購物車

添加到購物車

serialize rfi fault 序列化 framework param 重寫 cors default

1. 後端接口設計

請求方式 : POST /cart/

請求參數: JSON 或 表單

參數類型是否必須說明
sku_id int 商品sku id
count int 數量
selected bool 是否勾選,默認勾選

返回數據: JSON

參數類型是否必須說明
sku_id int 商品sku id
count int 數量
selected bool 是否勾選,默認勾選

訪問此接口,無論用戶是否登錄,前端請求都需攜帶請求頭Authorization,由後端判斷是否登錄

2. 後端實現

因為前端可能攜帶cookie,為了保證跨域請求中,允許後端使用cookie,確保在配置文件有如下設置

CORS_ALLOW_CREDENTIALS = True

創建應用carts。

在carts/serialziers.py中創建序列化器

class CartSerializer(serializers.Serializer):
    """
    購物車數據序列化器
    """
    sku_id = serializers.IntegerField(label=‘sku id ‘, min_value=1)
    count = serializers.IntegerField(label=‘數量‘, min_value=1)
    selected = serializers.BooleanField(label=‘是否勾選‘, default=True)

    def validate(self, data):
        try:
            sku = SKU.objects.get(id=data[‘sku_id‘])
        except SKU.DoesNotExist:
            raise serializers.ValidationError(‘商品不存在‘)

        if data[‘count‘] > sku.stock:
            raise serializers.ValidationError(‘商品庫存不足‘)

        return data

編寫視圖:

註意:因為前端請求時攜帶了Authorization請求頭(主要是JWT),而如果用戶未登錄,此請求頭的JWT無意義(沒有值),為了防止REST framework框架在驗證此無意義的JWT時拋出401異常,在視圖中需要做兩個處理

  • 重寫perform_authentication()方法,此方法是REST framework檢查用戶身份的方法
  • 在獲取request.user屬性時捕獲異常,REST framework在返回user時,會檢查Authorization請求頭,無效的Authorization請求頭會導致拋出異常

在carts/views.py中創建視圖

class CartView(APIView):
    """
    購物車
    """
    def perform_authentication(self, request):
        """
        重寫父類的用戶驗證方法,不在進入視圖前就檢查JWT
        """
        pass

    def post(self, request):
        """
        添加購物車
        """
        serializer = 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_conn = get_redis_connection(‘cart‘)
            pl = redis_conn.pipeline()
            # 記錄購物車商品數量
            pl.hincrby(‘cart_%s‘ % user.id, sku_id, count)
            # 記錄購物車的勾選項
            # 勾選
            if selected:
                pl.sadd(‘cart_selected_%s‘ % user.id, sku_id)
            pl.execute()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            # 用戶未登錄,在cookie中保存
            # {
            #     1001: { "count": 10, "selected": true},
            #     ...
            # }
            # 使用pickle序列化購物車數據,pickle操作的是bytes類型
            cart = request.COOKIES.get(‘cart‘)
            if cart is not None:
                cart = pickle.loads(base64.b64decode(cart.encode()))
            else:
                cart = {}

            sku = cart.get(sku_id)
            if sku:
                count += int(sku.get(‘count‘))

            cart[sku_id] = {
                ‘count‘: count,
                ‘selected‘: selected
            }

            cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()

            response = Response(serializer.data, status=status.HTTP_201_CREATED)

            # 設置購物車的cookie
            # 需要設置有效期,否則是臨時cookie
            response.set_cookie(‘cart‘, cookie_cart, max_age=constants.CART_COOKIE_EXPIRES)
            return response

在carts中新建constants.py 常量文件

# 購物車cookie的有效期
CART_COOKIE_EXPIRES = 365 * 24 * 60 * 60

添加到購物車