【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'

原文地址:https://www.cnblogs.com/midworld/p/13629755.html