美多商城项目购物车模块实现

购物车模块实现

  • 添加到购物车: 对前端发送的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
    
展开阅读全文

没有更多推荐了,返回首页