美多商城項目購物車模塊實現

購物車模塊實現

  • 添加到購物車: 對前端發送的sku_id, count, selected進行相應的存儲

    # 先定義序列化器, 用途是對sku_id,count進行驗證
    class CartSerializer(serializers.Serializer):
        sku_id = serializers.IntegerField(min_value=1)
        count = serializers.IntegerField(min_value=1)
        selected = serializers.BooleanField(default=True)
        def validate(self,attrs):
            try:
                sku = SKU.objects.get(id=atttrs['sku_id'])
    		except:
                raise serializers.ValidationError('商品不存在')
    		if count > sku.stock:
                raise serializers.ValidationError('庫存不足')
    		return attrs
        
    # 定義視圖, 實現業務邏輯
    class CartView(APIView):
        # 重寫驗證方法, 不在進入視圖前就檢查JWT
        def perform_authentication(self, request):
            pass
        # 在商品詳情頁添加購物車
        def post(self,request):
    		serializer=CartSerializer(data=request.data)
    		serializer.is_valid(raise_exception=True)
            # 獲取數據
    		sku_id=request.data.get('sku_id')
    		count=request.data.get('count')
    		selected=request.data.get('selected')
    		# 判斷用戶登錄
            try:
                user=request.user
    		except:
                user=None
    		if user:
                # 在redis中存儲,redis存儲的數據不需要加密
                conn = get_redis_connection('cart')
                conn.hincrby('cart_%s'%user.id,sku_id,count)
                if selected:
                    conn.sadd('cart_selected_%s'%user.id,sku_id)
    		   return Response(serializer.data)
            else:
                cart=request.COOKIES.get('cart')
                if cart:
                    cart=pickle.loads(base64.b64decode(cart))
    		   else:
                	cart={}
    		   sku = cart.get(sku_id)
                if sku:
                    count+=int(sku.get('count'))
                    cart[sku_id]={
                        'count':count,
                        'selected':selected
                    }
    		   cart_cookie=base64.b64encode(pickle.dumps(cart))
                response=Response(serializer.data)
                response.set_cookie('cart',cart_cookie,60*60*24)
                return response
                
            
    
  • 查詢購物車數據, 對redis或者cookie中存儲的數據進行讀取和展示

    # 序列化器用作返回需要的sku對象字段
    class CartSKUSerializer(serializers.ModelSerializer):
        count = serializers.IntegerField()
        selected = serializers.BooleanField()
        class Meta:
            model = SKU
            fields = ('id', 'count', 'name', 'default_image_url', 'price', 'selected')
            
    # 在上個視圖中寫get方法
    class CartView(APIView):
        ...
        def get(self,request):
            # 判斷用戶是否登錄
            try:
                user = request.user
            except Exception:
                user = None
    	    if user:
                conn = get_redis_connection('cart')
                redis_skus = conn.hgetall('cart_%s'%user.id)
                redis_selecteds=conn.smembers('cart_selected_%s'%user.id)
                cart={}
                for sku_id,count in redis_skus.items():
                    cart[sku_id]={
                        'count':count,
                        'selected':sku_id in redis_selecteds
                    }
    		else:
                 cart = request.COOKIES.get('cart')
                 if cart:
                     cart=pickle.loads(base64.b64.decode(cart))
                 else:
                     cart={}
    		skus = SKU.objects.filter(id__in=cart.keys())
             for sku in skus:
                 sku.count=cart[sku.id]['count']
    			sku.selected=cart[sku.id]['selected']
    		serializer=CartSKUSerializer(skus,many=True)
            return Response(serializer.data)
    
  • 修改是對redis和cookie中存儲數據的修改

    class CartView(APIView):
        ...
        # 接受數據, 驗證數據
        def put(self, request):
            serializer = CartSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            sku_id = serilaizer.validated_data.get('sku_id')
            count = serilaizer.validated_data.get('count')
            selected = serilaizer.validated_data.get('selected')
            try:
                user = request.user
            except:
                user = None
            if user:
                conn = get_redis_connection('cart')
                conn.hset('cart_%s'%user.id, sku_id, count)
                if selected:
                    conn.sadd('cart_selected_%s'%user.id, sku_id)
                else:
                    conn.srem('cart_selected_%s'%user.id, sku_id)
                return Response(serializer.data)
            else:
                cart = request.COOKIES.get('cart')
                if cart:
                    cart=pickle.loads(base64.b64decode(cart))
                else:
                    cart={}
                cart[sku_id]={
                        'count':count,
                        'selected':selected
                    }
                cookie_cart=pickle.dumps(base64.b64encode(cart))
                response=Response(serializer.data)
                response.set_cookie('cart',cookie_cart,60*60*24)
                return response
    
  • 刪除購物車數據, 根據sku_id刪除redis和cookie中的數據

     class CartView(APIView):
        ...
    
        def delete(self, request):
            # 這裏手動進行數據認證
            sku_id = request.data.get('sku_id')
            try:
                sku = SKU.objects.get(id=sku_id)
    	    except:
                return Response('商品id不存在')
            # 進行用戶驗證
            try:
                user=request.user
    	    except:
                user=None
    	    if user:
                conn=get_redis_connection('cart')
                conn.hdel('cart_%s'%user.id, sku_id)
                conn.srem('cart_selected_%s'%user.id, sku_id)
                return Response('OK')
            else:
                cart=request.COOKIES.get('cart')
                if cart:
                    cart=pickle.loads(base64.b64decode(cart))
                    if sku_id in cart:
                        del cart[sku_id]
                        cart=base64.b64encode(pickle.dumps(cart))
                        response=.set_cookie('cart',cart,60*60*24)
                return Response('OK')
    
  • 購物車全選, 是前端發送selected請求, 後端統一修改存儲的sku_id的狀態

    # 首先是序列化器的驗證, 驗證前端發送的selected數據是否正確
    class CartSelectAllSerializer(serializers.Serializer):
        selected=serializers.BooleanField()
        
    # 視圖的作用是業務邏輯的處理了
    class CartSelectAllView(APIView):
        # 重寫父類的用戶驗證方法,不在進入視圖前就檢查JWT
        def peiform_authentication(self, request):
            pass
        
        def put(self, request):
            # 驗證數據
            serializer = CartSelectAllSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            selected = serializer.validated_data['selected']
            
            # 驗證用戶是否登錄
            try:
                user = request.user
    		except:
                user=None
    		
            # 如果用戶登錄了
            if user is not None and user.is_authenticated:
                conn=get_redis_connection('cart')
                # 這是取出一個sku_id和count組成的字典
                cart_dict = conn.hgetall('cart_%s'%user.id)
                # 取出所有sku_id
                sku_ids = cart_dict.keys()
                # 如果是全選的話
                if selected:
                    conn.sadd('cart_selected_%s'%user.id, *sku_ids)
                # 如果是全不選的話
    		   else:
                    conn.srem('cart_selected_%s'%user.id, *sku_ids)
                return Response('OK')
                    
    	    else:
                # 修改cookie
                cart=request.COOKIES.get('cart')
                cart=pickle.loads(base64.b64decode(cart))
                response=Response('OK')
                if cart:
                    cart_new={}
                    for sku_id, data in cart.item():
                        cart_new[sku_id]={
                            'count':data['count'],
                            'selected':selected
                        }
                    cart=base64.b64encode(pickle.dumps(cart_new))
                    response.set_cookie('cart',cart,60*60*24)
                return response   
    
  • 合併登錄購物車, 合併普通登錄和QQ登錄, 所以封裝函數實現

    # 函數需要參數request,user,response
    # request獲取cookie,user是user.id,response是響應對象
    def merge_cart_cookie_to_rdeis(request,user,response):
        # 取出cart數據
        cart=request.COOKIES.get('cart')
        cart=pickle.loads(base64.b64decode(cart))
        # 把數據分開存儲在redis中
        if cart:
            cart_dict={}
            sku_list=[]
            sku_remove=[]
            for sku_id,data in cart.items():
                cart_dict[sku_id]=data['count']
                if data['selected']:
                	sku_list.append(sku_id)
                else:
                    sku_remove.append(sku_id)
            if cart_dict:
                conn=get_redis_connection('cart')
                conn.hset('cart_%s'%user.id, cart_dict)
                if sku_list:
                    conn.sadd('cart_selected_%s'%user.id, *sku_list)
                if sku_remove:
                    conn.srem('cart_selected_%s'%user.id, *sku_remove)
    	response.delete_cookie('cart')
        return response
    
  • 爲了在登錄中獲取user對象, 要修改登錄視圖的功能

    class UserAuthorizeView(ObtainJSONWebToken):
        def post(self,request,*args,**kwargs):
            response=super().post(request,*args,**kwargs)
            serializer=self.get_serializer(data_request.data)
            if serializer.is_valid():
                user=serializer.validated_data.get('user')
                # 對response進行了修改
                response=merge_cart_cookie_to_redis(request, user, response)
            return response 
    
  • 在創建QQ用戶時, 改寫序列化器, 在視圖對象中補充user對象屬性, 以便在視圖中使用

    def create(self,validated_data):
        ...
        self.context['view'].user=user
        return user
    
    # 修改views視圖, 尤其是post方法
    def post(self, request, *args, **kwargs):
        response=merge_cart_cookie_to_redis(request, self.user, response)
        return response
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章