zoukankan      html  css  js  c++  java
  • 【Vue+DRF 生鲜电商】首页轮播图、缓存和限速(十)

    1. 首页轮播图

    首先用 xadmin 设置首页轮播图,并设置播放顺序。

    1、goods/serializers.py

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

    2、goods/views.py

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

    3、MxShop/urls.py

    router.register(r'banners', BannerViewSet, basename='banner')  # 首页轮播图
    

    2. 首页新品

    goods/filters.py 中新增 is_new 字段,因为商品模型中 is_new 字段区分商品是否为新品,只需进行过滤即可:

    class GoodsFilter(django_filters.rest_framework.FilterSet):
        """
        商品过滤
        """
        # name 为要过滤的字段,lte 为执行的行为,这里为小于等于本店价格
        ....
    
        class Meta:
            model = Goods
            fields = ['pricemin', 'pricemax', 'top_category', 'is_hot', 'is_new']
    

    另外需要在后台中设置新品商品。

    3. 首页热搜词

    1、goods/serializers.py

    class HotSearchWordsSerializer(serializers.ModelSerializer):
        """搜索栏下方热搜关键词"""
    
        class Meta:
            model = HotSearchWords
            fields = '__all__'
    

    2、goods/views.py

    class HotSearchWordsViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
        """搜索栏下方热搜关键词"""
        serializer_class = HotSearchWordsSerializer
        queryset = HotSearchWords.objects.all().order_by('-index')
    

    3、MxShop/urls.py

    router.register(r'hotsearchs', HotSearchWordsViewSet, basename='hotsearchs')   # 搜索栏下方热搜关键词
    

    4. 首页分类显示

    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()
    
        sub_cat = CategorySerializer2(many=True)  # 取二级商品分类
        ad_goods = serializers.SerializerMethodField()  # 广告商品
    
        def get_ad_goods(self, obj):
            good_json = {}
            ad_goods = IndexAd.objects.filter(category_id=obj.id, )
            if ad_goods:
                good_ins = ad_goods[0].goods
                # 在 serializer 调用 serializer,需要添加上下文 context,嵌套 serializer 也必须加
                # serializer 返回的时候一定要加 .data 才是 json 数据
                good_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
    
            return good_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、MxShop/urls.py

    router.register(r'indexgoods', IndexCategoryViewSet, basename='indexgoods')  # 首页系列商品展示
    

    广告位商品需要后台设置。

    5. 商品点击数和收藏数

    两者可以查看 goods 数据表中 click_num、fav_num 数目变化。

    5.1 商品点击数

    商品点击数与获取商品详情为同一接口,只需重写 retrieve() 即可实现 goods/views.py

    class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
        """
        商品列表页
        """
        ....
    
        def retrieve(self, request, *args, **kwargs):
            """商品点击数 +1"""
            instance = self.get_object()
            instance.click_num += 1
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    

    5.2 收藏数

    5.2.1 重写 perform_create 和 perform_destory

    包括收藏和取消收藏,分别对应操作数据库加一、减一操作 user_operation/views.py

    class UserFavViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin,
                         mixins.DestroyModelMixin, mixins.RetrieveModelMixin):
        """
        用户商品收藏
        ListModelMixin:收藏列表
        CreateModelMixin:收藏
        DestroyModelMixin:取消(删除)收藏,相应地要删除数据库中数据
        """
        # serializer_class = UserFavSerializer
        queryset = UserFav.objects.all()
    
        # IsAuthenticated:必须登录用户;IsOwnerOrReadOnly:必须是当前登录的用户
        permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    
        # 用户认证
        authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    
        # 搜索的字段
        lookup_field = 'goods_id'
    
        def get_serializer_class(self):
            """
            动态设置 serializer,get 时获取用户收藏详情
            :return:
            """
            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)
    
        # def perform_create(self, serializer):
        #     """用户收藏的商品数量 +1"""
        #     instance = serializer.save()    # instance相当于UserFav
        #     goods = instance.goods
        #     goods.fav_num += 1
        #     goods.save()
    
        # def perform_destory(self, instance):
        #     """用户收藏的商品数量 -1"""
        #     goods = instance.goods
        #     goods.fav_num -= 1
        #     if goods.fav_num < 0:
        #         goods.fav_num = 0
        #     goods.save()
    

    注意:注意不能为负数!

    5.2.2 信号

    使用信号,代码分离性更好,这里只需接收 model 模型 create、delete 发出来的信号即可:

    1、新建 user_operation/signals.py

    from django.db.models.signals import post_save, post_delete
    from django.dispatch import receiver
    
    from user_operation.models import UserFav
    
    
    @receiver(post_save, sender=UserFav)
    def create_UserFav(sender, instance=None, created=False, **kwargs):
        """
        用户收藏 +1
        post_save:接收信号的方式、sender:接收信号的 model
        :param sender:
        :param instance:
        :param created:
        :return:
        """
        # 是否新建,因为 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):
        """用户收藏 -1"""
        goods = instance.goods
        goods.fav_num -= 1
        if goods.fav_num < 0:
            goods.fav_num = 0
        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
    

    6. 商品库存和销量

    6.1 库存数量

    商品库存修改会导致的情况:

    • 新增商品到购物车
    • 修改购物车商品数量
    • 删除购物车记录
    • 订单取消,库存增加

    trade/views.py

    class ShoppingCartViewSet(viewsets.ModelViewSet):
        """
        购物车功能:
        list:获取购物车详情
        create:加入购物车
        delete:删除购物车记录
        """
        ....
    
        def perform_create(self, serializer):
            """添加到购物车,库存数 -1"""
            shop_cart = serializer.save()
            goods = shop_cart.goods
            goods.goods_num -= shop_cart.nums
            goods.save()
    
        def perform_destroy(self, instance):
            """从购物车中删除,库存数 +1"""
            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
            # 保存之前的数据
            save_record = serializer.save()
            # 变化的数量
            nums = save_record.nums - existed_nums
            goods = save_record.goods
            goods.goods_num -= nums
            goods.save()
    

    6.2 商品销量

    只有当支付成功后,销量才增加 trade/views.py

    class AlipayView(APIView):
        """支付相关"""
    
        def post(self, request):
            """
            处理支付宝的 notify_url
            支付宝服务器主动通知商户服务器里指定的页面http/https路径
            :param request:
            :return:
            """
            ....
    
            # 验证成功
            if verify_re is True:
                ....
                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.save()
    
                # 返回一个 success 给支付宝,若不返回支付宝会一直发送订单支付成功的消息
                return Response("success")
    

    7. 缓存和接口限速

    7.1 内存缓存

    http://chibisov.github.io/drf-extensions/docs/#caching

    1、安装拓展:pip install drf-extensions

    2、设置缓存时间 settings.py

    # drf-extensions配置
    REST_FRAMEWORK_EXTENSIONS = {
        'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 10  # 缓存全局过期时间(60 * 10 表示10分钟)
    }
    

    3、给商品添加缓存 goods/views.py

    class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
        """
        商品列表页
        """
        pass
    

    GoodsListViewSet 视图中添加 CacheResponseMixin必须放在第一位

    注意:内存缓存重启浏览器失效,因此可以采用 redis 缓存

    7.2 配置 redis 缓存后端

    1、安装 django-redis

    pip install django-redis
    
    # hiredis 的作用是提升 redis 解析性能
    pip install hiredis
    

    2、配置 settings

    # redis 缓存相关
    # 使用 redis 数据库 1 存储缓存结果
    REDIS_URL = 'redis://192.168.131.131:6379/1'
    
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": REDIS_URL,
            'TIMEOUT': 300, # 超时时间
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                'PASSWORD_CLASS': 'redis.connection.HiredisParser',
            },
            'CONNECTION_POOL_CLASS': 'redis.connection.BlockingConnectionPool',
        }
    }
    

    3、访问 http://192.168.131.131:8000/goods/,查看前后两次加载的速度:

    服务器上查看 redis 有没有缓存结果:

    [root@localhost hj]# redis-cli
    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> keys *
    1) ":1:1e399649bf49833ffcb5facd7bd7c7d7"
    2) ":1:1b4b0a4b5386c3b1e949f67333311750"
    3) ":1:7e04a81fa423f272e0115f47a426a125"
    4) ":1:47956e9d5293aecd0bd9b696fea21dc7"
    

    也可以使用 redis 客户端工具:https://github.com/qishibo/AnotherRedisDesktopManager/releases

    注意:其他需要添加缓存的请自行添加,以上只以缓存商品为例

    7.3 接口频率限制

    接口访问速度过快,会对服务器造成很大压力,可以对接口访问频率进行限制,分为全局限制所有接口和单独对某个接口进行限制。

    全局限制

    配置 settings

    REST_FRAMEWORK = {
        # throttle 全局限制接口访问频率
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle',  # 未登录用户,限制其 IP
            'rest_framework.throttling.UserRateThrottle',  # 已登录用户,根据其 token 判断
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '3/minute',  # 匿名用户,每分钟 3 次
            'user': '5/minute',  # 登录用户,每分钟 5次
        },
    }
    

    单个 API 接口限速

    1、settings

    REST_FRAMEWORK = {
        # throttle 全局限制接口访问频率
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.ScopedRateThrottle',  # 限制用户对于每个视图的访问频次,使用ip或user id。
        ],
        'DEFAULT_THROTTLE_RATES': {
            'goods_list': '600/minute'
        }
    

    2、在视图函数中添加限制:goods/views.py

    class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
        """
        商品列表页
        """
        ....
        throttle_scope = 'goods_list'
    

  • 相关阅读:
    网络性能测试工具iperf详解
    linux下的抓包工具tshark和 tcpdump用法
    linux下网络发包工具
    >/dev/null 2>&1 的作用
    linux screen 命令详解
    wget 命令操作实例
    Centos Linux release 7.2.15ll (core) yum 安装java环境
    日志监听
    java 身份证工具类
    java 生成微信的二维码 工具类
  • 原文地址:https://www.cnblogs.com/midworld/p/13629755.html
Copyright © 2011-2022 走看看