文章目录

  • 一、首页功能完善
    • 1.轮播图实现
    • 2.新品功能开发
    • 3.商品系列分类展示功能
  • 二、商品相关数量功能实现
    • 1.商品点击数、收藏数功能实现
    • 2.商品库存量和销量功能实现
  • 三、DRF缓存设置
    • 1.使用drf-extensions配置缓存
    • 2.配置Redis缓存
  • 四、DRF通过throttling设置api的访问速率

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

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

一、首页功能完善

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

1.轮播图实现

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

class BannerSerializer(serializers.ModelSerializer):class Meta:model = Bannerfields = '__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')

测试如下:

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

前端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/`) }

此时再进行测试如下:

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

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

演示如下:

显然,在手动添加新品之后,新品数据即同步,请求的参数中包含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循环显示出来。

访问示意如下:

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_namedef __str__(self):return self.nameclass 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_namedef __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 = Bannerfields = '__all__'class BrandSerializer(serializers.ModelSerializer):class Meta:model = GoodsCategoryBrandfields = '__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.datadef get_ad_goods(self, obj):goods_json = {}ad_goods = IndexAd.objects.filter(category_id=obj.id)if ad_goods:good_instance = ad_goods[0].goodsgoods_json = GoodsSerializer(good_instance, many=False).datareturn goods_jsonclass Meta:model = GoodsCategoryfields = '__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')

进行测试:

可以看到,再添加数据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.datadef get_ad_goods(self, obj):goods_json = {}ad_goods = IndexAd.objects.filter(category_id=obj.id)if ad_goods:good_instance = ad_goods[0].goodsgoods_json = GoodsSerializer(good_instance, many=False, context={'request': self.context['request']}).datareturn goods_jsonclass Meta:model = GoodsCategoryfields = '__all__'

此时再查看如下:

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

前端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/`) }

访问演示如下:

此时可以正常访问。

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

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 = GoodsSerializerpagination_class = GoodsPaginationfilter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]filter_class = GoodsFiltersearch_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 += 1instance.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 = UserFavSerializerauthentication_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 UserFavDetailSerializerelif self.action == 'create':return UserFavSerializerreturn UserFavSerializerdef perform_create(self, serializer):'''重写实现收藏数'''instance = serializer.save()goods = instance.goodsgoods.fav_num += 1goods.save()

演示如下:

显然,点击数在刷新页面后即加一,收藏数在用户未收藏的前提下收藏后也会加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 Tokenfrom .models import UserFav@receiver(post_save, sender=UserFav)
def create_userfav(sender, instance=None, created=False, **kwargs):if created:goods = instance.goodsgoods.fav_num += 1goods.save()@receiver(post_delete, sender=UserFav)
def delete_userfav(sender, instance=None, created=False, **kwargs):goods = instance.goodsgoods.fav_num -= 1goods.save()

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

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

演示如下:

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

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 = ShoppingCartSerializerlookup_field = 'goods_id'def get_serializer_class(self):if self.action == 'list':return ShoppingCartDetailSerializerelse:return ShoppingCartSerializerdef 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.goodsgoods.goods_num -= shop_cart.numsgoods.save()def perform_destroy(self, instance):'''删除购物车更新库存量'''goods = instance.goodsgoods.goods_num += instance.numsgoods.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.numssaved_record = serializer.save()nums = saved_record.nums - existed_numsgoods = saved_record.goodsgoods.goods_num -= numsgoods.save()

演示如下:

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

销售量一般是在支付成功后更新,所以在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.goodsgoods.sold_num += order_good.goods_numgoods.save()order.pay_status = trade_statusorder.trade_no = trade_noorder.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 responsereturn 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.goodsgoods.sold_num += order_good.goods_numgoods.save()order.pay_status = trade_statusorder.trade_no = trade_noorder.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 responsereturn HttpResponseRedirect('http://127.0.0.1:8080/#/app/shoppingcart/cart')

演示如下:

显然,已经实现了在提交订单并付款后,销售量更新。

三、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 = GoodsSerializerpagination_class = GoodsPaginationfilter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]filter_class = GoodsFiltersearch_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 += 1instance.save()serializer = self.get_serializer(instance)return Response(serializer.data)

进行测试如下:

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

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

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

再进行测试如下:

可以看到,在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数据库为空,不存在数据,此时进行访问测试如下:

可以看到,相同的请求多次访问,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 = GoodsSerializerpagination_class = GoodsPaginationfilter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]filter_class = GoodsFiltersearch_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 += 1instance.save()serializer = self.get_serializer(instance)return Response(serializer.data)

此时进行访问测试如下:

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

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

  1. Django+Vue开发生鲜电商平台之10.购物车、订单管理和支付功能

    文章目录 一.购物车功能实现 1.加入购物车功能实现 2.修改购物车数量功能实现 3.和Vue结合实现购物车功能 二.订单功能实现 1.订单管理接口 2.Vue接入订单接口 三.支付宝支付接口完成 1 ...

  2. Django+Vue开发生鲜电商平台之1.项目介绍

    文章目录 一.项目概览 二.项目技术要点 三.项目预览 我永远相信只要永不放弃,我们还是有机会的.最后,我们还是坚信一点,这世界上只要有梦想,只要不断努力,只要不断学习,不管你长得如何,不管是这样,还 ...

  3. Django+Vue开发生鲜电商平台之3.数据模型设计和资源导入

    文章目录 一.项目初始化 二.数据模型设计 1.用户数据模型设计 2.商品数据模型设计 3.交易数据模型设计 4.用户操作数据模型设计 三.xadmin后台管理系统的配置 四.数据迁移和数据导入 1. ...

  4. Django+Vue开发生鲜电商平台之2.开发环境搭建

    文章目录 一.PyCharm的安装和简单使用 二.MySQL和Navicat的安装和使用 三.Python的安装 四.虚拟环境的安装和配置 五.Vue开发环境搭建 一.PyCharm的安装和简单使用 ...

  5. [附源码]java毕业设计社区生鲜电商平台

    项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...

  6. [附源码]计算机毕业设计JAVA社区生鲜电商平台

    [附源码]计算机毕业设计JAVA社区生鲜电商平台 项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(Inte ...

  7. JAVA毕设项目社区生鲜电商平台(java+VUE+Mybatis+Maven+Mysql)

    JAVA毕设项目社区生鲜电商平台(java+VUE+Mybatis+Maven+Mysql) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Web ...

  8. Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲

    Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲 Java生鲜电商平台:   微服务是当前非常流行的技术框架,通过服务的小型化.原子化以及分布式架构的弹性伸缩和高可用性, ...

  9. Java生鲜电商平台-App系统架构开发与设计

    Java生鲜电商平台-App系统架构开发与设计 说明:阅读此文,你可以学习到以下的技术分享 1.Java生鲜电商平台-App架构设计经验谈:接口的设计 2.Java生鲜电商平台-App架构设计经验谈: ...

最新文章

  1. c++ 三次多项式拟合_非线性回归模型(一)--多项式回归
  2. python导入csv文件-jupyter 导入csv文件方式
  3. 注意力机制中的Q、K和V的意义
  4. TCP/UDP网络性能测试工具 - Netperf (zz) ..网络测试工具
  5. 泛函编程(4)-深入Scala函数类
  6. 校招刷题---java选择题笔记01
  7. Python爬取京东图书销量榜
  8. Jenkins(03):配置Jenkins自动发送邮件
  9. 23种设计模式:(一)创建者模型
  10. Linuxnbsp;下摄像头驱动支持情况(…
  11. Windows10下载到U盘怎么安装?
  12. NPDP产品经理证书在中国有用吗?
  13. 计算机windows怎么开启,Win10怎么打开我的电脑_Win10正式版怎么打开我的电脑?-192路由网...
  14. C#重点知识详解(转)
  15. “讯飞杯”合肥市第三十届青少年信息学奥林匹克竞赛(小学组)试题
  16. 读书百遍,其义自见,要不得
  17. 网络虚拟化——SR-IOV
  18. JS如何判断滚动条是否滚到底部
  19. [附源码]java毕业设计石林县石漠化信息查询分析系统
  20. ZBrush怎样快速雕刻出头发(一)

热门文章

  1. html中样式里面有符号,css样式 + 特殊符号
  2. 树莓派 电脑通过界面远程控制
  3. 安卓车机root改流浪地球_你想知道的《流浪地球》的问题,都在这里了
  4. Excel表格转到Word中,保持表格不变形,不超边缘纸张范围
  5. PHP+ mysql实现注册登录功能
  6. 二叉树遍历 递归/非递归/morris
  7. PHP后端接入短信接口为用户发送通知短信
  8. pr 调整图层缩放移动无效
  9. 2018滴水LUA手游脚本制作实战视频教程
  10. cad.net 图层隐藏 IsHidden 用法 eDuplicateRecordName 报错