Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

CuterCorley
• 阅读 1703

青,取之于蓝而青于蓝;冰,水为之而寒于水。 ——《荀子·劝学》

Github和Gitee代码同步更新https://github.com/PythonWebProject/Django_Fresh_Ecommercehttps://gitee.com/Python_Web_Project/Django_Fresh_Ecommerce

一、首页功能完善

首页待完善的功能包括轮播图、新品尝鲜、系列商品等。

1.轮播图实现

轮播图包括3张图片,链接对应3个商品,先在apps/goods/serializers.py中定义序列化如下:

class BannerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Banner
        fields = '__all__'

再在views.py中定义视图如下:

class BannerViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    '''
    list:
        轮播图列表
    '''
    queryset = Banner.objects.filter(is_delete=False).order_by('index')
    serializer_class = BannerSerializer

再在urls.py中定义路由如下:

# 配置轮播图路由
router.register(r'banners', BannerViewSet, basename='banners')

测试如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

可以看到,开始没有轮播图数据,经过在管理后台添加数据后,即同步数据。

前端src/views/index/banners.vue如下:

<swiper :options="swiperOption">
    <swiper-slide v-for="item in banners" :key="item.goods">
    <router-link :to="'/app/home/productDetail/'+item.goods" target = _blank> <img :src="item.image" alt="" /></router-link>
    </swiper-slide>
    <div class="swiper-pagination" slot="pagination"></div>
</swiper>

methods:{
    getBanner(){
    bannerGoods()
        .then((response)=> {
        console.log(response)
        //跳转到首页页response.body面
        this.banners = response.data
        })
        .catch(function (error) {
        console.log(error);
        });
    }
},

created(){
    this.getBanner();
}

在初始化时调用getBanner()方法,在调用bannerGoods接口请求数据,请求到的数据再通过for循环展示出来。

api.js修改如下:

//获取轮播图
export const bannerGoods = params => { return axios.get(`${local_host}/banners/`) }

此时再进行测试如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,请求的图片数据已从本地加载,并且点击轮播图片会调皮转到相应的商品链接。

2.新品功能开发

在定义商品模型时定义了is_new字段表示是否是新品,再实现新品功能时需要用到该字段,直接使用Goods接口并在filters.py过滤器中定义即可,如下:

class GoodsFilter(django_filters.rest_framework.FilterSet):
    '''商品过滤类'''
    name = django_filters.CharFilter(field_name="name", lookup_expr='contains')
    pricemin = django_filters.NumberFilter(field_name="market_price", lookup_expr='gte')
    pricemax = django_filters.NumberFilter(field_name="market_price", lookup_expr='lte')
    top_category = django_filters.NumberFilter(method='top_category_filter')

    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 = ['name', 'pricemin', 'pricemax', 'is_hot', 'is_new']

演示如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,在手动添加新品之后,新品数据即同步,请求的参数中包含is_new=true

前端src/views/index/news.vue如下:

<li class="prolist-cent clearfix have_num" v-for="item in newopro">
    <div class="prolist-l fl">
    <router-link :to="'/app/home/list/'+item.id"  target = _blank> <a  :title="item.name" class="imgBox">
    <img :src="item.goods_front_image" style="height: 158px;width: 158px;" class="zom" :alt="item.name">
    </a></router-link>
    </div>
    <div class="prolist-r fl">
        <h3 class="ft14 c333 bold">
        <router-link :to="'/app/home/list/'+item.id"  :title="item.name" target = _blank>{{item.name}}</router-link>
        </h3>
        <p><em class="c333"></em>{{item.goods_brief}}</p><div>
        <span class="p-price"><em class="fastbuy_price">¥{{item.shop_price}}元</em><del>原价:¥{{item.market_price}}元</del></span>
        <a href="" class="p-buy fr ibg">立即抢购</a>
        <span class="p-time fr">销量:{{item.sold_num}}件</span>
    </div>
    </div>
</li>

methods:{
    getOpro(){
        getGoods({
        "is_new":"true"
        })
        .then((response)=> {
            //跳转到首页页response.body面
            this.newopro = response.data.results
        })
        .catch(function (error) {
            console.log(error);
        });
    }
},

created(){
    this.getOpro();
}

可以看到,在初始化时,调用getOpro()方法,在调用getGoods接口时传入参数is_new,来获取新品,与之前获取商品调用的接口相同,获取到数据后通过for循环显示出来。

访问示意如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

3.商品系列分类展示功能

商品系列分类包括左侧的导航栏和右侧的商品列表,大类对应多个品牌、大类对应多个小类、大类对应多个商品,即包含3个一对多关系,在定义序列化时需要嵌套定义

为了实现嵌套,在定义GoodsCategoryBrand模型时需要指定related_name属性,如下:

class GoodsCategoryBrand(models.Model):
    '''品牌名'''
    category = models.ForeignKey(GoodsCategory, verbose_name='商品类目', related_name='brands', null=True, on_delete=models.SET_NULL)
    name = models.CharField(default='', max_length=30, verbose_name='品牌名', help_text='品牌名')
    desc = models.TextField(default='', max_length=200, verbose_name='品牌描述', help_text='品牌描述')
    image = models.ImageField(max_length=200, upload_to='brands/')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '品牌'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class IndexAd(models.Model):
    category = models.ForeignKey(GoodsCategory, verbose_name='商品类目', related_name='category', null=True, on_delete=models.SET_NULL)
    goods = models.ForeignKey(Goods, verbose_name='商品', related_name='goods', null=True, on_delete=models.SET_NULL)

    class Meta:
        verbose_name = '首页商品类别广告'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.goods.name

完成后需要进行数据映射。

为了在创建brand时只显示一级类别,在adminx.py中定义GoodsBrandAdmin类时重写了get_context()方法,其中获取到category字段只取category_type为1的数据,如下:

class GoodsBrandAdmin(object):
    list_display = ["category", "image", "name", "desc"]

    def get_context(self):
        context = super(GoodsBrandAdmin, self).get_context()
        if 'form' in context:
            context['form'].fields['category'].queryset = GoodsCategory.objects.filter(category_type=1)
        return context

定义序列化如下:

class BannerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Banner
        fields = '__all__'


class BrandSerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategoryBrand
        fields = '__all__'


class IndexCategorySerializer(serializers.ModelSerializer):
    brands = BrandSerializer(many=True)
    goods = serializers.SerializerMethodField()
    sub_cat = SecCategorySerializer(many=True)
    ad_goods = serializers.SerializerMethodField()

    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)
        return goods_serializer.data

    def get_ad_goods(self, obj):
        goods_json = {}
        ad_goods = IndexAd.objects.filter(category_id=obj.id)
        if ad_goods:
            good_instance = ad_goods[0].goods
            goods_json = GoodsSerializer(good_instance, many=False).data
        return goods_json

    class Meta:
        model = GoodsCategory
        fields = '__all__'

可以看到,定义了多个一对多的关系和一个一对一的关系,视图如下:

class IndexCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    '''
    list:
        商品分类数据
    '''

    queryset = GoodsCategory.objects.filter(is_delete=False, is_tab=True, name__in=['生鲜食品', '酒水饮料'])
    serializer_class = IndexCategorySerializer

定义路由如下:

# 配置首页商品系列路由
router.register(r'indexgoods', IndexCategoryViewSet, basename='indexgoods')

进行测试: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

可以看到,再添加数据brands和ad_goods之前这两类数据为空,添加之后数据同步。

但是同时也可以看到,在商品的image属性的值即图片链接中未自动添加域名,这是因为进行嵌套序列化默认不会添加域名,需要给字段设置context属性,配置如下:

class IndexCategorySerializer(serializers.ModelSerializer):
    brands = BrandSerializer(many=True)
    goods = serializers.SerializerMethodField()
    sub_cat = SecCategorySerializer(many=True)
    ad_goods = serializers.SerializerMethodField()

    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

    def get_ad_goods(self, obj):
        goods_json = {}
        ad_goods = IndexAd.objects.filter(category_id=obj.id)
        if ad_goods:
            good_instance = ad_goods[0].goods
            goods_json = GoodsSerializer(good_instance, many=False, context={'request': self.context['request']}).data
        return goods_json

    class Meta:
        model = GoodsCategory
        fields = '__all__'

此时再查看如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,已经将域名显示出来。

前端src/views/index/series-list.vue如下:

<div class="series_info">
    <div class="series_name name_hufu">
        <h2>{{items.name}}</h2>
    </div>
    <ul class="brand">

        <li v-for="brand in items.brands">
            <router-link :to="'/app/home/list/'+brand.id" >
            <a :title="brand.name" target="_blank">
                <img :src="brand.image" :alt="brand.name" style="display: inline;">
            </a>
            </router-link>
        </li>
    </ul>
    <div class="brand_cata">
        <router-link  v-for="label in items.sub_cat" :key="label.id" :title="label.name"   :to="'/app/home/list/'+label.id"  >
        {{label.name}}
        </router-link>
    </div>

</div>
    <div class="series_pic">
        <router-link :to="'/app/home/productDetail/'+items.ad_goods.id" target = _blank>
            <img :src="items.ad_goods.goods_front_image" width="340" height="400">
        </router-link>
    </div>

    <div class="pro_list">
        <ul class="cle">
            <li v-for="list in items.goods">
                    <router-link :to="'/app/home/productDetail/'+list.id" target = _blank>
                    <p class="pic">
                        <img :src="list.goods_front_image" style="display: inline;">
                        </p>
                    <h3>{{list.name}}</h3>
                    <p class="price">
                        ¥{{list.shop_price}}元
                    </p>
                </router-link>
            </li>

        </ul>
    </div>
</div>

methods:{
    getList(){
        queryCategorygoods()
        .then((response)=> {
            //跳转到首页页response.body面
            console.log(response)
            this.list = response.data
        })
        .catch(function (error) {
            console.log(error);
        });
    }
},
created(){
    this.getList();
}

在初始化时调用getList()方法,调用queryCategorygoods接口获取到数据后通过便利展示品牌和商品,并将广告商品展示出来。

api.js中接口如下:

//获取商品类别信息
export const queryCategorygoods = params => { return axios.get(`${local_host}/indexgoods/`) }

访问演示如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

此时可以正常访问。

二、商品相关数量功能实现

1.商品点击数、收藏数功能实现

商品点击数通过在视图GoodsListViewSet中重写RetrieveModelMixin类的retrieve(request, *args, **kwargs)方法实现,每请求一次click_num加1,如下:

class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    '''
    商品列表页,并实现分页、搜索、过滤、排序
    list:
        商品列表
    retrieve:
        商品详情
    '''

    queryset = Goods.objects.filter(is_delete=False).order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_class = GoodsFilter
    search_fields = ['name', 'goods_brief', 'goods_desc']
    ordering_fields = ['sold_num', 'shop_price']

    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)

收藏数需要在apps/user_operation/views.py中的GoodsListViewSet视图中重写CreateModelMixin类的perform_create(serializer)方法,如下:

class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
    '''
    list:
        用户收藏列表
    create:
        创建用户收藏
    retrieve:
        用户收藏详情
    destroy:
        删除用户收藏
    '''
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    serializer_class = UserFavSerializer
    authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
    lookup_field = 'goods_id'

    def get_queryset(self):
        return UserFav.objects.filter(user=self.request.user, is_delete=False)

    def get_serializer_class(self):
        '''动态设置序列化'''
        if self.action == 'list':
            return UserFavDetailSerializer
        elif self.action == 'create':
            return UserFavSerializer
        return UserFavSerializer

    def perform_create(self, serializer):
        '''重写实现收藏数'''
        instance = serializer.save()
        goods = instance.goods
        goods.fav_num += 1
        goods.save()

演示如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,点击数在刷新页面后即加一,收藏数在用户未收藏的前提下收藏后也会加1。

收藏量除了通过以上方式实现,还可以通过信号量实现,这可以达到增加收藏和减少收藏的效果,注释掉之前在视图中实现增加收藏量的代码,在apps/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 .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()


@receiver(post_delete, sender=UserFav)
def delete_userfav(sender, instance=None, created=False, **kwargs):
    goods = instance.goods
    goods.fav_num -= 1
    goods.save()

apps/user_operation/apps.py完善如下:

from django.apps import AppConfig


class UserOperationConfig(AppConfig):
    name = 'user_operation'
    verbose_name = '用户操作管理'

    def ready(self):
        import user_operation.signals

演示如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,达到了增减收藏量的效果。

2.商品库存量和销量功能实现

引起商品库存量变化的操作一般有3种:

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

显然,都与购物车有关,需要完善apps/trade/views.py中的ShoppingCartViewSet视图类,新增商品到购物车重写CreateModelMixin类的perform_create(serializer)方法,修改购物车数量重写UpdateModelMixin类的perform_update(serializer)方法,删除购物车记录重写DestroyModelMixin类的perform_destroy(instance)方法,如下:

class ShoppingCartViewSet(viewsets.ModelViewSet):
    '''
    list:
        购物车列表
    create:
        加入购物车
    update:
        购物车修改
    delete:
        删除购物车
    '''

    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
    serializer_class = ShoppingCartSerializer
    lookup_field = 'goods_id'

    def get_serializer_class(self):
        if self.action == 'list':
            return ShoppingCartDetailSerializer
        else:
            return ShoppingCartSerializer

    def get_queryset(self):
        return ShoppingCart.objects.filter(user=self.request.user, is_delete=False)

    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.filter(is_delete=False).get(id=serializer.instance.id)
        existed_nums = existed_record.nums
        saved_record = serializer.save()
        nums = saved_record.nums - existed_nums
        goods = saved_record.goods
        goods.goods_num -= nums
        goods.save()

演示如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,可以正常更新库存数量。

销售量一般是在支付成功后更新,所以在apps/trade/views.py中的AliPayView中实现:

class AliPayView(APIView):
    '''
    get:
        处理支付宝return_url请求
    post:
        处理支付宝notify_url请求
    '''

    alipay = AliPay(
        appid=ali_app_id,
        app_notify_url=None,
        app_private_key_string=open(app_private_key_path).read(),
        alipay_public_key_string=open(alipay_public_key_path).read(),
        sign_type="RSA2",
        debug=True,
    )

    def get(self, request):

        data = dict(request.GET.items())
        signature = data.pop("sign", None)
        print(data)
        success = self.alipay.verify(data, signature)
        order_sn = data.get('out_trade_no', None)
        print(success)
        trade_status = self.alipay.api_alipay_trade_query(out_trade_no=order_sn).get("trade_status", None)
        print(trade_status)
        if success and trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
            trade_no = data.get('trade_no', None)
            existed_orders = OrderInfo.objects.filter(order_sn=order_sn, is_delete=False)
            if existed_orders:
                for order in existed_orders:
                    order_goods = order.goods.all()
                    for order_good in order_goods:
                        goods = order_good.goods
                        goods.sold_num += order_good.goods_num
                        goods.save()
                    order.pay_status = trade_status
                    order.trade_no = trade_no
                    order.pay_time = datetime.now()
                    order.save()
                response = HttpResponseRedirect('http://127.0.0.1:8080/#/app/home/member/order')
                response.set_cookie('nextPath', 'pay', max_age=2)
                print('cookie', response.cookies)
                return response
        return HttpResponseRedirect('http://127.0.0.1:8080/#/app/shoppingcart/cart')

    def post(self, request):
        data = dict(request.POST.items())
        signature = data.pop("sign", None)
        success = self.alipay.verify(data, signature)
        order_sn = data.get('out_trade_no', None)
        trade_status = self.alipay.api_alipay_trade_query(out_trade_no=order_sn).get("trade_status", None)
        if success and trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
            trade_no = data.get('trade_no', None)
            existed_orders = OrderInfo.objects.filter(order_sn=order_sn, is_delete=False)
            print(len(existed_orders))
            if existed_orders:
                for order in existed_orders:
                    order_goods = order.goods.all()
                    for order_good in order_goods:
                        goods = order_good.goods
                        goods.sold_num += order_good.goods_num
                        goods.save()
                    order.pay_status = trade_status
                    order.trade_no = trade_no
                    order.pay_time = datetime.now()
                    order.save()
                response = HttpResponseRedirect('http://127.0.0.1:8080/#/app/home/member/order')
                response.set_cookie('nextPath', 'pay', max_age=2)
                print('cookie', response.cookies)
                return response
        return HttpResponseRedirect('http://127.0.0.1:8080/#/app/shoppingcart/cart')

演示如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发 显然,已经实现了在提交订单并付款后,销售量更新。

三、DRF缓存设置

1.使用drf-extensions配置缓存

在一般情况下,将一些经常访问的数据放入缓存中,可以加快网页响应的速度。对于变化小的数据,将其保存到缓存中请求时直接获取的成本要源于每次请求再重新计算获取的成本,所以使用缓存是很有必要的。 Django支持的缓存包括Memcached数据库高速缓存文件系统缓存本地内存缓存虚拟缓存等,DRF的缓存机制建立在Django的基础上,并进行了一些优化,这里采用的是已经封装好的drf-extensions(DRF扩展),对DRF进行了很多方面的功能扩展,其中就包括缓存功能,Github地址为https://github.com/chibisov/drf-extensions,缓存caching的文档说明地址为http://chibisov.github.io/drf-extensions/docs/#caching

使用之前需要通过命令pip install drf-extensions -i https://pypi.douban.com/simple安装,我们使用的主要是CacheResponseMixin,主要适用于retrievelist方法,这主要是查询操作,对于新建、修改等操作一般是不能使用缓存的。

对于商品,apps/goods/views.py中的GoodsListViewSet,使之继承自CacheResponseMixin,即可实现缓存,如下:

class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    '''
    商品列表页,并实现分页、搜索、过滤、排序
    list:
        商品列表
    retrieve:
        商品详情
    '''

    queryset = Goods.objects.filter(is_delete=False).order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_class = GoodsFilter
    search_fields = ['name', 'goods_brief', 'goods_desc']
    ordering_fields = ['sold_num', 'shop_price']

    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)

进行测试如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

可以看出,第一次请求http://127.0.0.1:8000/goods/用时354毫秒,而第二次请求只用了13毫秒,显然,用时大大减少。

在很多时候,我们还需要设置缓存的过期时间,而不是无限制地保留缓存,此时需要在settings.py中进行配置如下:

# drf-extensions配置
REST_FRAMEWORK_EXTENSIONS = {
    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 5
}

再进行测试如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

可以看到,在Django重启后,第1次请求用时865毫秒,第2次请求在5秒内缓存还未过期,因此请求较快,为33毫秒,第3次请求已经超过5秒,缓存过期,所以重新请求,用时576毫秒。

对于一般情况而言,公开数据可以使用缓存,而需要验证才能访问的私有数据则尽量不要使用缓存,可以根据自己的需要对不同的数据进行缓存配置。

2.配置Redis缓存

之前配置的缓存都是使用Local Memory Cache即本地内存进行缓存的,Django重启之后就会消失,Redis作为backend进行了扩展和优化。

缓存应该考虑请求内容的格式是HTML还是json,请求是否包含参数等等问题,这可以根据Redis的键值观察出来,需要使用第三方库django-redis,通过命令pip install django-redis -i https://pypi.douban.com/simple安装即可。

使用前,需要在settings.py中进行配置如下:

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

可以看到,需要在本地启动Redis服务。

在配置好Redis缓存后,连接Redis客户端,查询如下:

keys *

打印:

(empty list or set)

显然,此时Redis数据库为空,不存在数据,此时进行访问测试如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

可以看到,相同的请求多次访问,Redis也不会增加数据,只有进行了不同类型的请求才会增加数据,django-redis根据请求的不同类型生成key,以区分不同类型的请求。

四、DRF通过throttling设置api的访问速率

因为爬虫的存在,如果爬虫的速率过快、不考虑网站的承受能力,会对服务器造成很大的压力,甚至影响正常用户的访问,因此需要限制访问速率,对关键数据、对性能要求高的数据进行限速。

DRF自带了限速功能,直接使用throttling进行限速即可实现,throttling与权限类似,它决定是否应授权请求。节流指示临时状态,并用于控制客户端可以向API发出的请求的速率,一般对未经身份验证的请求进行限制,而对于经过身份验证的请求则进行限制较少。

需要在settings.py中进行配置如下:

# DRF配置
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '2/minute',
        'user': '3/minite'
    }
}

其中,AnonRateThrottle是对未登录用户的限制,通过IP判断;UserRateThrottle是对已登录用户的限制,通过Token或Session判断。

还需要在视图中进行配置,如下:

class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    '''
    商品列表页,并实现分页、搜索、过滤、排序
    list:
        商品列表
    retrieve:
        商品详情
    '''

    throttle_classes = [AnonRateThrottle, UserRateThrottle]
    queryset = Goods.objects.filter(is_delete=False).order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_class = GoodsFilter
    search_fields = ['name', 'goods_brief', 'goods_desc']
    ordering_fields = ['sold_num', 'shop_price']

    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)

此时进行访问测试如下: Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

显然,限速成功,并且对于未登录和已登录的限制不一样。

本文原文首发来自博客专栏Python Web开发实战,由本人转发至https://www.helloworld.net/p/b330cmKtj2ij5,其他平台均属侵权,可点击https://blog.csdn.net/CUFEECR/article/details/107797490查看原文,也可点击https://blog.csdn.net/CUFEECR浏览更多优质原创内容。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这