首页、商品数量、缓存和限速功能开发

将环境切换为本地,vue也切换为本地

轮播图

goods/serializers.py

class BannerSerializer(serializers.ModelSerializer):
    '''
    轮播图
    '''

    class Meta:
        model = Banner
        fields = "__all__"

goods/views.py

class BannerViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    首页轮播图
    """
    queryset = Banner.objects.all().order_by("index")
    serializer_class = BannerSerializer

urls.py

# 配置首页轮播图的url
router.register(r'banners', BannerViewset, base_name="banners")

新品推荐功能

在设计Goods model时候有一个字段is_new

    is_new = models.BooleanField(default=False, verbose_name="是否新品")

实现这个接口只要在goods/filters/GoodsFilter里面添加一个过滤就可以了

    class Meta:
        model = Goods
        fields = ['pricemin', 'pricemax','is_hot','is_new']

首页商品分类显示功能

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()
    # 在parent_category字段中定义的related_name="sub_cat"
    # 取二级商品分类
    sub_cat = CategorySerializer2(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:
            # 取到这个商品Queryset[0]
            good_ins = ad_goods[0].goods
            # 在serializer里面调用serializer的话,就要添加一个参数context(上下文request),
            # 否则图片链接是不完整的
            # 嵌套serializer必须加
            # serializer返回的时候一定要加 “.data” ,这样才是json数据
            goods_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
        return goods_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__"

goods/views.py

class IndexCategoryViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    首页商品分类数据
    """
    # 获取is_tab=True(导航栏)里面的分类下的商品数据
    queryset = GoodsCategory.objects.filter(is_tab=True, name__in=["生鲜食品", "酒水饮料"])
    serializer_class = IndexCategorySerializer

urls.py

# 首页系列商品展示url
router.register(r'indexgoods', IndexCategoryViewset, base_name="indexgoods")

热搜词

goods/serializers.py

class HotWordsSerializer(serializers.ModelSerializer):
    class Meta:
        model = HotSearchWords
        fields = "__all__"

goods/views.py

class HotSearchsViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    获取热搜词列表
    """
    queryset = HotSearchWords.objects.all().order_by("-index")
    serializer_class = HotWordsSerializer

urls.py

# 首页热搜词
router.register(r'hotsearchs', HotSearchsViewset, base_name="hotsearchs")

商品点击数和收藏数

GoodsListViewSet其中继承了mixins.RetrieveModelMixin
只需要重写retrieve方法即可

class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    '''
    商品列表页, 分页, 过滤, 排序
    '''
    queryset = Goods.objects.all().order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)

    # authentication_classes = (TokenAuthentication,)
    # 自定义过滤器
    filter_class = GoodsFilter
    # 搜索,默认模糊查询
    search_fields = ('name', 'goods_brief')
    # 排序
    ordering_fields = ('sold_num', 'shop_price')

    # 商品点击数 + 1
    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)

UserFavViewset继承了mixins.CreateModelMixin
重写perform_create方法即可

class UserFavViewset(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin):
    '''
    list:
        获取用户的所有收藏
    create:
        添加收藏
    destroy:
        取消收藏
    '''
    # permission是用来做权限判断的
    # IsAuthenticated:必须登录用户;IsOwnerOrReadOnly:必须是当前登录的用户
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    # auth使用来做用户认证的
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    # 搜索的字段
    lookup_field = 'goods_id'

    def get_queryset(self):
        # 只能查看当前登录用户的收藏,不会获取所有用户的收藏
        return UserFav.objects.filter(user=self.request.user)

    # 动态选择serializer
    def get_serializer_class(self):
        if self.action == "list":
            return UserFavDetailSerializer
        elif self.action == "create":
            return UserFavSerializer
        return UserFavSerializer

    # 用户收藏的商品数量+1
    def perform_create(self, serializer):
        instance = serializer.save()
        # 这里instance相当于UserFav model,通过它找到goods
        goods = instance.goods
        goods.fav_num += 1
        goods.save()

用信号量实现收藏数变化
delete和create的时候django model都会发送一个信号量出来,用信号量的方式代码分离性更好

user_operation/signals.py

from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver

from user_operation.models import UserFav


# post_save : model变化方式
# sender : 变动的model
@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()

user_operation/apps.py

from django.apps import AppConfig


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

    def ready(self):
        import user_operation.signals

商品库存和销量修改

引起商品库存数量变化的行为:

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

trade/views.py

class ShoppingCartViewset(viewsets.ModelViewSet):
    """
    购物车功能
    list:
        获取购物车详情
    create:
        加入购物车
    delete:
        删除购物记录
    """
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

    # serializer_class = ShopCartSerializer
    lookup_field = "goods_id"

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

    def get_serializer_class(self):
        if self.action == 'list':
            return ShopCartDetailSerializer
        else:
            return ShopCartSerializer

    # 库存数-n
    def perform_create(self, serializer):
        shop_cart = serializer.save()
        goods = shop_cart.goods
        goods.goods_num -= shop_cart.nums
        goods.save()

    # 库存数+n
    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_nums = existed_record.nums
        # 先保存之前的数据existed_nums
        saved_record = serializer.save()
        # 变化的数量
        nums = saved_record.nums - existed_nums
        goods = saved_record.goods
        goods.goods_num -= nums
        goods.save()

商品的销量只有在支付成功后才会 +n
trade/views.py
AlipayView/post方法

            # 查询数据库中订单记录
            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()
            # 需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
            return Response("success")

drf缓存

http://chibisov.github.io/drf-extensions/docs/#caching

pip install drf-extensions

简单使用
在GoodsListViewSet中添加缓存功能

from rest_framework_extensions.cache.mixins import CacheResponseMixin

# CacheResponseMixin一定要放在第一个位置

class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):

设置过期时间,settings里面

# 缓存配置
REST_FRAMEWORK_EXTENSIONS = {
    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60*60*8   # 多少秒过期,时间自己可以随便设定
}

这个缓存使用的是内存,每次重启之后就会失效

drf配置redis缓存

https://django-redis-chs.readthedocs.io/zh_CN/latest/

drf的throttle设置api的访问速率

针对爬虫
http://www.django-rest-framework.org/api-guide/throttling/

settings中配置

REST_FRAMEWORK = {
    # 限速设置
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',  # 未登陆用户
        'rest_framework.throttling.UserRateThrottle'  # 登陆用户
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '10/minute',  # 每分钟可以请求n次
        'user': '30/minute'  # 每分钟可以请求n次
    }
}

goods/views.py中使用

from rest_framework.throttling import UserRateThrottle,AnonRateThrottle

class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
  .
  .
  throttle_classes = (UserRateThrottle, AnonRateThrottle)