The number of 11- commodity, caching, rate limiting development

First, the number of commodities, caching, rate limiting development

1.1, carousel map interface and debugging vue

After analysis, rotation need FIG view, serializer, routing, and vue FBI

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   # specified Model 
        Fields = " __all__ is "   # the entire field is displayed

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")

 

 After adding a good Carousel commodity access interface, and data successfully returned vue FBI success

 

 

1.2, the development of new functional interface

Filters in the Product List page view added is_new option ( 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)

 

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

Guess you like

Origin www.cnblogs.com/lishuntao/p/11918377.html