• 11-商品数量、缓存、限速功能开发


    一、商品数量、缓存、限速功能开发

    1.1、轮播图接口实现和vue调试

    分析完,轮播图需要视图、序列化器、路由以及vue联调

    apps/goods/views.py:

    from .models import Goods,GoodsCategory,Banner
    from .serializers import GoodsSerializer,GoodsCategorySerializer,BannerSerializer
    
    
    class BannerViewset(mixins.ListModelMixin,viewsets.GenericViewSet):
        """
        获取轮播图列表
        """
        queryset = Banner.objects.all().order_by("index")
        serializer_class = BannerSerializer

    apps/goods/serializers.py:

    from .models import Goods,GoodsCategory,GoodsImage,Banner
    
    
    class BannerSerializer(serializers.ModelSerializer):
        class Meta:
            model = Banner  # 指明model
            fields = "__all__"  # 将全部字段显示出来

    Mxshop/urls.py:

    from rest_framework.routers import DefaultRouter
    
    from goods.views import GoodsListViewSet,CategoryViewset,BannerViewset
    
    
    router = DefaultRouter()
    
    #轮播图
    router.register(r"banners",BannerViewset,base_name="banners")

     添加好轮播商品后访问接口,成功返回数据与vue联调成功

     

    1.2、新品功能接口开发

    在商品列表页视图中的过滤器中添加is_new选项(goods/filters.py

    class GoodsFilter(django_filters.rest_framework.FilterSet):
        """
        商品过滤器
        """
        pricemin = django_filters.NumberFilter(field_name="shop_price",lookup_expr="gte",help_text="最低价格")
        pricemax = django_filters.NumberFilter(field_name="shop_price", lookup_expr="lte",help_text="最大价格")
        name = django_filters.CharFilter(field_name="name", lookup_expr="icontains",help_text="名字")
        #这里过滤的字段要和前端传过来的字段一样
        top_category = django_filters.NumberFilter(method="top_category_filter",help_text="搜索栏关键字")
    
        def top_category_filter(self,queryset,name,value):
            return queryset.filter(Q(category_id=value)|Q(category__parent_category_id=value)|Q(category__parent_category__parent_category_id=value))
    
        class Meta:
            model = Goods
            fields = ["pricemin","pricemax","name","top_category","is_hot","is_new"]

     

     

     

    1.3、首页商品分类显示功能

    goods/views.py:

    class IndexCategoryViewset(mixins.ListModelMixin,viewsets.GenericViewSet):
        """
        首页商品分类数据
        """
        queryset = GoodsCategory.objects.filter(is_tab=True,name__in=["生鲜食品","酒水饮料"])
        serializer_class = IndexCategorySerializer

    goods/serializers.py:

    from rest_framework import serializers
    from django.db.models import Q
    
    from .models import Goods,GoodsCategory,GoodsImage,Banner,GoodsCategoryBrand,IndexAd
    
    
    class GoodsCategorySerializer3(serializers.ModelSerializer):
        """
        商品三级分类
        """
        class Meta:
            model = GoodsCategory  # 指明model
            fields = "__all__"  # 将全部字段显示出来
    
    
    
    class GoodsCategorySerializer2(serializers.ModelSerializer):
        """
        商品二级分类
        """
        sub_cat = GoodsCategorySerializer3(many=True)
        class Meta:
            model = GoodsCategory  # 指明model
            fields = "__all__"  # 将全部字段显示出来
    
    
    class GoodsCategorySerializer(serializers.ModelSerializer):
        """
        商品类别序列化
        """
        sub_cat = GoodsCategorySerializer2(many=True)
        class Meta:
            model = GoodsCategory  # 指明model
            fields = "__all__"  # 将全部字段显示出来
    
    
    class GoodsImageSerializer(serializers.ModelSerializer):
        class Meta:
            model = GoodsImage
            fields = ("image",)
    
    
    class GoodsSerializer(serializers.ModelSerializer):
        """
        在这里可以利用新写的字段来覆盖已有字段
        """
        category = GoodsCategorySerializer() #在这里实例化外键的序列化器,就可以完成
        images = GoodsImageSerializer(many=True) #名字一定要和related_name="images"中的名字一样
        class Meta:
            model = Goods #指明model
            #fields = ['category', 'goods_sn', 'name', 'click_num','sold_num','fav_num','add_time'] #指明字段
            fields = "__all__" #将全部字段显示出来
    
    
    class BannerSerializer(serializers.ModelSerializer):
        class Meta:
            model = Banner  # 指明model
            fields = "__all__"  # 将全部字段显示出来
    
    
    class BrandsSerializer(serializers.ModelSerializer):
        class Meta:
            model = GoodsCategoryBrand
            fields = "__all__"
    
    
    
    class IndexCategorySerializer(serializers.ModelSerializer):
        brands = BrandsSerializer(many=True)
        goods = serializers.SerializerMethodField()
        sub_cat = GoodsCategorySerializer2(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:
                goods_ins = ad_goods[0].goods
                ##########关于图片加域名问题:mixins会自动加载,序列化不会原因判断request是否存在#####################
                goods_json = GoodsSerializer(goods_ins,many=False,context={"request":self.context["request"]})
            return goods_json.data
    
        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__"

    新建品牌模型类goods/models.py,新建好之后数据库迁移生成表

    class IndexAd(models.Model):
        category = models.ForeignKey(GoodsCategory, related_name="category",null=True, blank=True, verbose_name="商品类目", on_delete=models.CASCADE)
        goods = models.ForeignKey(Goods,related_name="goods",on_delete=models.CASCADE)
    
        class Meta:
            verbose_name = '首页商品类别广告'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.goods.name

    注册后台品牌管理goods.adminx.py:

    class IndexAdAdmin(object):
        list_display = ["category", "goods"]
    
    
    xadmin.site.register(IndexAd, IndexAdAdmin)

    设置访问路由MxShop/urls.py:

    #首页商品系列数据
    router.register(r"indexgoods",IndexCategoryViewset,base_name="indexgoods")

    后台管理系统添加品牌相关数据:

     与vue联调首页展示:

    1.4、商品点击数、收藏数修改

       为了将商品点击数与收藏数进行修改,点击数可以直接重载Mixins的具体类方法,但为了使得我们逻辑清楚,收藏数利用django的信号量机制来完成收藏数修改。每当点击进去详情页的时候,就点击数加一。因此重载mixins.RetrieveModelMixin类的retrieve方法。

    goods/views.py:

    class GoodsListViewSet(mixins.ListModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):
        """
        商品列表页 分页 搜索 过滤 排序
        """
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
        pagination_class = GoodsSetPagination
        filter_backends = [DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter]
        #这是精确搜索过滤,我们需要的是模糊搜索
        # filterset_fields = ['name', 'shop_price']
        filter_class = GoodsFilter
        search_fields = ("name","goods_brief","goods_desc")
        ordering_fields = ("shop_price","sold_num","add_time")
    
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.click_num += 1 #重写retrive方法,自定义详情页操作
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)

    收藏数利用django信号机制完成user_operation/signals.py:

    from django.db.models.signals import post_save,post_delete
    from django.dispatch import receiver
    from rest_framework.authtoken.models import Token
    from django.contrib.auth import get_user_model
    
    from user_operation.models import UserFav
    
    @receiver(post_save, sender=UserFav)
    def create_userfav(sender, instance=None, created=False, **kwargs):
        if created:
            goods = instance.goods
            goods.fav_num += 1
            goods.save()
            # Token.objects.create(user=instance)  用了JWT的方式,就不用Token
    
            #完成这个我们还要在apps中配置
    @receiver(post_delete, sender=UserFav)
    def delete_userfav(sender, instance=None, created=False, **kwargs):
            goods = instance.goods
            goods.fav_num -= 1
            goods.save()

    user_operation/apps.py:

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

     

     这样就完成点击数与收藏数的修改了。

    1.5、商品库存和销量修改

    当加入购物车的时候,库存数量减少,当删除购物车的时候库存增加,当完成订单支付成功的时候,销量增加。

    trade/views.py:

    class ShoppingCartViewset(viewsets.ModelViewSet):
        """
        购物车功能
        list:
            获取购物车详情
        create:
            加入购物车
        delete:
            删除购物车
        """
        permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
        authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
        serializer_class = ShoppingCartSerializers
        lookup_field = "goods_id"#传商品Id过来
        def get_serializer_class(self):
            if self.action == "list":
                return ShopCartDetailSerilizer
            else:
                return ShoppingCartSerializers
    
        def perform_create(self, serializer):
            """
            添加购物车库存数量会减少
            """
            shop_cart = serializer.save()
            goods = shop_cart.goods
            goods.goods_num -= shop_cart.nums
            goods.save()
    
        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_num = existed_record.nums
            saved_record = serializer.save()
            nums = saved_record.nums - existed_num
            goods = saved_record.goods
            goods.goods_num -= nums
            goods.save()
    
        def get_queryset(self):
            return ShoppingCart.objects.filter(user=self.request.user)

    商品销量的修改应该在订单支付成功后修改trade/views.py:

    from rest_framework.views import APIView
    from utils.alipay import AliPay
    from MxShop.settings import ali_pub_key_path, private_key_path
    from rest_framework.response import Response
    class AliPayview(APIView):
        def get(self, request):
            """
            处理支付宝的return_url返回
            :param request:
            :return:
            """
            processed_dict = {}
            for key, value in request.GET.items():
                processed_dict[key] = value
    
            sign = processed_dict.pop("sign", None)
    
            alipay = AliPay(
                appid="2016101600698988",
                app_notify_url="http://127.0.0.1:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://127.0.0.1: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_orders.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_SUCCESS"
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
    
                response = redirect("index")
                response.set_cookie("nextPath", "pay", max_age=3)
                return response
            else:
                response = redirect("index")
                return response
    
        def post(self, request):
            """
            处理支付宝的notify_url
            :param request:
            :return:
            """
            processed_dict = {}
            for key, value in request.POST.items():
                processed_dict[key] = value
    
            sign = processed_dict.pop("sign", None)
    
            alipay = AliPay(
                appid="",
                app_notify_url="http://127.0.0.1:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://127.0.0.1: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()
    
                return Response("success")

    数据库修改商品库存数量,测试成功

    1.6、DRF的缓存设置

     安装包(3种方法安装):

    pip3 install drf-extensions
    
    pip3 install -i https://pypi.douban.com/simple drf-extensions  #豆瓣源镜像安装
    
    pip3 install https://github.com/chibisov/drf-extensions/archive/master.zip

    It is common to cache standard viewset retrieve and list methods.:(意思是说我们访问商品列表和商品详情的时候要用到缓存(公共部分),)

    Mixin example usage:Mixin的使用例子)按照例子以及结合上面使用缓存,我们可以直接使用

    from myapps.serializers import UserSerializer
    from rest_framework_extensions.cache.mixins import CacheResponseMixin
    
    class UserViewSet(CacheResponseMixin, viewsets.ModelViewSet):
        serializer_class = UserSerializer

    goods/views.py:

    from rest_framework_extensions.cache.mixins import CacheResponseMixin
    
    #将缓存添加到类继承中
    class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):
        """
        商品列表页 分页 搜索 过滤 排序
        """
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
        pagination_class = GoodsSetPagination
        filter_backends = [DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter]
        #这是精确搜索过滤,我们需要的是模糊搜索
        # filterset_fields = ['name', 'shop_price']
        filter_class = GoodsFilter
        search_fields = ("name","goods_brief","goods_desc")
        ordering_fields = ("shop_price","sold_num","add_time")
    
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.click_num += 1 #重写retrive方法,自定义详情页操作
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)

    Mxshop、settings.py(设置缓存过期时间):

    REST_FRAMEWORK_EXTENSIONS = {
        'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 15
    }

    测试一对比,第二次是不是比第一次快了很多啊。

     

    1.7、DRF的Redis缓存

    1、安装django-redis

    pip3 install -i https://pypi.douban.com/simple django-redis

     为了使用 django-redis , 你应该将你的 django cache setting 改成这样(Mxshop/settings.py):

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

    为了使用redis一定要将本地的redis服务端启动起来。然后访问网页。最后去redis客户端查看数据

    1.8、DRF的throttle设置api的访问速率(REST Framework Throttling)

      为了防止爬虫以及非正常用户多次访问服务器我们需要对api设置访问速率。Mxshop/settings:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
            'rest_framework.authentication.BasicAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication',
        ],
        "DEFAULT_SCHEMA_CLASS" : "rest_framework.schemas.AutoSchema",
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle',#不登陆情况
            'rest_framework.throttling.UserRateThrottle'  #登录情况
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '100/day', #每天访问多少次 secondminutehour or day 参数可以是这些
            'user': '1000/day'  
        }
    }

    我将获取商品列表详情页设置为限速,为了测试并将限速一分钟两次以及登录三次访问goods/views.py:

    from rest_framework.throttling import AnonRateThrottle,UserRateThrottle
    
    
    class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):
        """
        商品列表页 分页 搜索 过滤 排序
        """
        throttle_classes = (AnonRateThrottle,UserRateThrottle)    #限速设置
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
        pagination_class = GoodsSetPagination
        filter_backends = [DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter]
        #这是精确搜索过滤,我们需要的是模糊搜索
        # filterset_fields = ['name', 'shop_price']
        filter_class = GoodsFilter
        search_fields = ("name","goods_brief","goods_desc")
        ordering_fields = ("shop_price","sold_num","add_time")
    
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.click_num += 1 #重写retrive方法,自定义详情页操作
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)

     改动设置里限速测试,测试成功

  • 相关阅读:
    iOS 静态、全局变量、常量
    原子性atomic/nonatomic
    iOS数组遍历
    iOS开发过程中易犯的小错误
    mac开启Airdrop的硬件要求
    Activity Monitor 闪退 & 无法进入睡眠
    在Linux中连接android设备
    网格布局(GridLayout) 行数与列数
    $符号报not defing 报错
    eclipse鼠标变成十字符号
  • 原文地址:https://www.cnblogs.com/lishuntao/p/11918377.html
Copyright © 2020-2023  润新知