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)

 

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

posted @ 2019-12-04 15:04  一知.半解  阅读(...)  评论(...编辑  收藏