zoukankan      html  css  js  c++  java
  • 11生鲜超市(首页、商品数量、缓存和限速功能开发)

    Django REST framework+Vue 打造生鲜超市(十二)

    目录

    十三、首页、商品数量、缓存和限速功能开发

     13.1.轮播图接口实现

    首先把pycharm环境改成本地的,vue中local_host也改成本地 

    (1)goods/serializer

    class BannerSerializer(serializers.ModelSerializer):
        '''
        轮播图
        '''
        class Meta:
            model = Banner
            fields = "__all__"

    (2)goods/views.py

    class BannerViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        """
        首页轮播图
        """
        queryset = Banner.objects.all().order_by("index")
        serializer_class = BannerSerializer

    (3)url

    # 配置首页轮播图的url
    router.register(r'banners', BannerViewset, base_name="banners")

    在后台添加首页轮播图图片

     13.2.新品接口功能开发

    在设计Goods model时候有一个字段is_new

    is_new = models.BooleanField("是否新品",default=False)

    实现这个接口只要在goods/filters/GoodsFilter里面添加一个过滤就可以了

        class Meta:
            model = Goods
            fields = ['pricemin', 'pricemax','is_hot','is_new']

    在后台设置几个商品 is_new

    13.3.首页商品分类显示功能

     首先是大类,然后里面有

    • 商品商标(多个)
    • 大类下的二级类
    • 广告商品
    • 所有商品

    (1)goods/serializers.py

    class BrandSerializer(serializers.ModelSerializer):
        '''
        大类下面的宣传商标
        '''
        class Meta:
            model = GoodsCategoryBrand
            fields = "__all__"
    
    
    class IndexCategorySerializer(serializers.ModelSerializer):
        #某个大类的商标,可以有多个商标,一对多的关系
        brands = BrandSerializer(many=True)
        # good有一个外键category,但这个外键指向的是三级类,直接反向通过外键category(三级类),取某个大类下面的商品是取不出来的
        goods = serializers.SerializerMethodField()
        # 在parent_category字段中定义的related_name="sub_cat"
        # 取二级商品分类
        sub_cat = CategorySerializer2(many=True)
        # 广告商品
        ad_goods = serializers.SerializerMethodField()
    
        def get_ad_goods(self, obj):
            goods_json = {}
            ad_goods = IndexAd.objects.filter(category_id=obj.id, )
            if ad_goods:
                #取到这个商品Queryset[0]
                good_ins = ad_goods[0].goods
                #在serializer里面调用serializer的话,就要添加一个参数context(上下文request),嵌套serializer必须加
                # serializer返回的时候一定要加 “.data” ,这样才是json数据
                goods_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
            return goods_json
    
        #自定义获取方法
        def get_goods(self, obj):
            # 将这个商品相关父类子类等都可以进行匹配
            all_goods = Goods.objects.filter(Q(category_id=obj.id) | Q(category__parent_category_id=obj.id) | Q(
                category__parent_category__parent_category_id=obj.id))
            goods_serializer = GoodsSerializer(all_goods, many=True, context={'request': self.context['request']})
            return goods_serializer.data
    
        class Meta:
            model = GoodsCategory
            fields = "__all__"

    (2)goods/views.py

    class IndexCategoryViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        """
        首页商品分类数据
        """
        # 获取is_tab=True(导航栏)里面的分类下的商品数据
        queryset = GoodsCategory.objects.filter(is_tab=True, name__in=["生鲜食品", "酒水饮料"])
        serializer_class = IndexCategorySerializer

    (3)url

    # 首页系列商品展示url
    router.register(r'indexgoods', IndexCategoryViewset, base_name="indexgoods")

    13.4.商品点击数和收藏数

    (1)点击数

    GoodsListViewSet其中继承了mixins.RetrieveModelMixin(获取商品详情)

    源码

    class RetrieveModelMixin(object):
        """
        Retrieve a model instance.
        """
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)

    我们只要重写他的retrieve方法就可以了

    goods/views

      #商品点击数 + 1
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.click_num += 1
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
        '''
        list:
            商品列表,分页,搜索,过滤,排序
        retrieve:
            获取商品详情
        '''
    
        # authentication_classes = (TokenAuthentication,)
        #这里必须要定义一个默认的排序,否则会报错
        queryset = Goods.objects.all().order_by('id')
        # 分页
        pagination_class = GoodsPagination
        #序列化
        serializer_class = GoodsSerializer
        filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
    
        # 设置filter的类为我们自定义的类
        #过滤
        filter_class = GoodsFilter
        #搜索
        search_fields = ('name', 'goods_brief', 'goods_desc')
        #排序
        ordering_fields = ('sold_num', 'shop_price')
    
        #商品点击数 + 1
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.click_num += 1
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    GoodListViewSet

    (2)收藏数

    前面已经写了UserFavViewset,其中继承了mixins.CreateModelMixin,添加收藏实际就是创建数据库

    这里重写它的perform_create方法就可以了

    user_operation/view.py

    # 用户收藏的商品数量+1
        def perform_create(self, serializer):
            instance = serializer.save()
            # 这里instance相当于UserFav model,通过它找到goods
            goods = instance.goods
            goods.fav_num += 1
            goods.save()
    class UserFavViewset(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin):
        '''
        用户收藏
        '''
        #permission是用来做权限判断的
        # IsAuthenticated:必须登录用户;IsOwnerOrReadOnly:必须是当前登录的用户
        permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)
        #auth使用来做用户认证的
        authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication)
        #搜索的字段
        lookup_field = 'goods_id'
    
        #动态选择serializer
        def get_serializer_class(self):
            if self.action == "list":
                return UserFavDetailSerializer
            elif self.action == "create":
                return UserFavSerializer
            return UserFavSerializer
    
        def get_queryset(self):
            #只能查看当前登录用户的收藏,不会获取所有用户的收藏
            return UserFav.objects.filter(user=self.request.user)
    
        # 用户收藏的商品数量+1
        def perform_create(self, serializer):
            instance = serializer.save()
            # 这里instance相当于UserFav model,通过它找到goods
            goods = instance.goods
            goods.fav_num += 1
            goods.save()
    UserFavViewset

    (3)用信号量实现

    delete和create的时候django model都会发送一个信号量出来,用信号量的方式代码分离性更好

    收藏数+1和-1

    (1)user_operation/signal.py

    # users_operation/signals.py
    
    from django.db.models.signals import post_save,post_delete
    from django.dispatch import receiver
    from user_operation.models import UserFav
    
    # post_save:接收信号的方式
    #sender: 接收信号的model
    @receiver(post_save, sender=UserFav)
    def create_UserFav(sender, instance=None, created=False, **kwargs):
        # 是否新建,因为update的时候也会进行post_save
        if created:
            goods = instance.goods
            goods.fav_num += 1
            goods.save()
    
    @receiver(post_delete, sender=UserFav)
    def delete_UserFav(sender, instance=None, created=False, **kwargs):
            goods = instance.goods
            goods.fav_num -= 1
            goods.save()

    (2)user_operation/apps.py

    from django.apps import AppConfig
    
    class UserOperationConfig(AppConfig):
        name = 'user_operation'
        verbose_name = "操作管理"
    
        def ready(self):
            import user_operation.signals

    13.5.商品库存和销量修改

    库存数量

    商品库存数量的行为:

    • 新增商品到购物车
    • 修改购物车数量
    • 删除购物车记录

    trade/views.py

    # 库存数-1
        def perform_create(self, serializer):
            shop_cart = serializer.save()
            goods = shop_cart.goods
            goods.goods_num -= shop_cart.nums
            goods.save()
    
        # 库存数+1
        def perform_destroy(self, instance):
            goods = instance.goods
            goods.goods_num += instance.nums
            goods.save()
            instance.delete()
    
        # 更新库存,修改可能是增加页可能是减少
        def perform_update(self, serializer):
            #首先获取修改之前的库存数量
            existed_record = ShoppingCart.objects.get(id=serializer.instance.id)
            existed_nums = existed_record.nums
            # 先保存之前的数据existed_nums
            saved_record = serializer.save()
            #变化的数量
            nums = saved_record.nums-existed_nums
            goods = saved_record.goods
            goods.goods_num -= nums
            goods.save()
    class ShoppingCartViewset(viewsets.ModelViewSet):
        """
        购物车功能
        list:
            获取购物车详情
        create:
            加入购物车
        delete:
            删除购物记录
        """
        permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
        authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
        serializer_class = ShopCartSerializer
        #商品的id
        lookup_field = "goods_id"
    
        def get_serializer_class(self):
            if self.action == 'list':
                return ShopCartDetailSerializer
            else:
                return ShopCartSerializer
    
        #获取购物车列表
        def get_queryset(self):
            return ShoppingCart.objects.filter(user=self.request.user)
    
        # 库存数-1
        def perform_create(self, serializer):
            shop_cart = serializer.save()
            goods = shop_cart.goods
            goods.goods_num -= shop_cart.nums
            goods.save()
    
        # 库存数+1
        def perform_destroy(self, instance):
            goods = instance.goods
            goods.goods_num += instance.nums
            goods.save()
            instance.delete()
    
        # 更新库存,修改可能是增加页可能是减少
        def perform_update(self, serializer):
            #首先获取修改之前的库存数量
            existed_record = ShoppingCart.objects.get(id=serializer.instance.id)
            existed_nums = existed_record.nums
            # 先保存之前的数据existed_nums
            saved_record = serializer.save()
            #变化的数量
            nums = saved_record.nums-existed_nums
            goods = saved_record.goods
            goods.goods_num -= nums
            goods.save()
    ShoppingCartViewset

    商品销量

    商品的销量只有在支付成功后才会 +1

    trade/views.py

                    for order_good in order_goods:
                        goods = order_good.goods
                        goods.sold_num += order_good.goods_num
                        goods.save()
    class AlipayView(APIView):
        def get(self, request):
            """
            处理支付宝的return_url返回
            """
            processed_dict = {}
            # 1. 获取GET中参数
            for key, value in request.GET.items():
                processed_dict[key] = value
            # 2. 取出sign
            sign = processed_dict.pop("sign", None)
    
            # 3. 生成ALipay对象
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            verify_re = alipay.verify(processed_dict, sign)
    
            # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
            if verify_re is True:
                order_sn = processed_dict.get('out_trade_no', None)
                trade_no = processed_dict.get('trade_no', None)
                trade_status = processed_dict.get('trade_status', None)
    
                existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
                for existed_order in existed_orders:
                    existed_order.pay_status = trade_status
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
    
                response = redirect("/index/#/app/home/member/order")
                return response
    
            else:
                response = redirect("index")
                return response
    
        def post(self, request):
            """
            处理支付宝的notify_url
            """
            #存放post里面所有的数据
            processed_dict = {}
            #取出post里面的数据
            for key, value in request.POST.items():
                processed_dict[key] = value
            #把signpop掉,文档有说明
            sign = processed_dict.pop("sign", None)
    
            #生成一个Alipay对象
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            #进行验证
            verify_re = alipay.verify(processed_dict, sign)
    
            # 如果验签成功
            if verify_re is True:
                #商户网站唯一订单号
                order_sn = processed_dict.get('out_trade_no', None)
                #支付宝系统交易流水号
                trade_no = processed_dict.get('trade_no', None)
                #交易状态
                trade_status = processed_dict.get('trade_status', None)
    
                # 查询数据库中订单记录
                existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
                for existed_order in existed_orders:
                    # 订单商品项
                    order_goods = existed_order.goods.all()
                    # 商品销量增加订单中数值
                    for order_good in order_goods:
                        goods = order_good.goods
                        goods.sold_num += order_good.goods_num
                        goods.save()
    
                    # 更新订单状态
                    existed_order.pay_status = trade_status
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
                #需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
                return Response("success")
    AlipayView

    13.6.drf的缓存设置

    为了加速网站的访问速度,将一些数据放到缓存当中,取数据的时候首先去缓存中去,然后再去数据库中取

    我们用drf的一个扩展来实现缓存,github上面的使用说明:http://chibisov.github.io/drf-extensions/docs/#caching

     (1)安装

    pip install drf-extensions

    (2)使用方法

    导入

    from rest_framework_extensions.cache.mixins import CacheResponseMixin

    在GoodsListViewSet中添加缓存功能

    #CacheResponseMixin一定要放在第一个位置
    
    class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):

    设置过期时间,settings里面

    #缓存配置
    REST_FRAMEWORK_EXTENSIONS = {
        'DEFAULT_CACHE_RESPONSE_TIMEOUT': 5   #5s过期,时间自己可以随便设定
    }

    这个缓存使用的是内存,每次重启之后就会失效

    13.7.drf配置redis缓存

    使用django-redis第三方库:http://django-redis-chs.readthedocs.io/zh_CN/latest/#id8    (文档说明)

     (1)安装

    pip install django-redis

    (2)settings

    # redis缓存
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        }
    }

    13.8.drf的throttle设置api的访问速率

    为了防止爬虫对服务器造成的重大压力,对数据进行访问速率限制就显得非常的重要了

    官网使用说明:http://www.django-rest-framework.org/api-guide/throttling/

    (1)settings中配置

    REST_FRAMEWORK = {
        #限速设置
        'DEFAULT_THROTTLE_CLASSES': (
                'rest_framework.throttling.AnonRateThrottle',   #未登陆用户
                'rest_framework.throttling.UserRateThrottle'    #登陆用户
            ),
        'DEFAULT_THROTTLE_RATES': {
            'anon': '3/minute',         #每分钟可以请求两次
            'user': '5/minute'          #每分钟可以请求五次
        }
    }

    (2)goods/views.py中使用

    from rest_framework.throttling import UserRateThrottle,AnonRateThrottle

    class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
      .
      .
      throttle_classes = (UserRateThrottle, AnonRateThrottle)

     

    end。。。

  • 相关阅读:
    Compression algorithm (deflate)
    tcpip数据包编码解析(chunk and gzip)_space of Jialy_百度空间
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    gzip压缩算法: gzip 所使用压缩算法的基本原理
    Decompressing a GZip Stream with Zlib
    Frequently Asked Questions about zlib
    how to decompress gzip stream with zlib
    自己动手写web服务器四(web服务器是如何通过压缩数据,web服务器的gzip模块的实现)
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    C语言抓http gzip包并解压 失败 C/C++ ChinaUnix.net
  • 原文地址:https://www.cnblogs.com/edeny/p/10020404.html
Copyright © 2011-2022 走看看