Django REST framework+Vue 打造生鮮超市(十二)


目錄

生鮮超市(一)    生鮮超市(二)    生鮮超市(三)   

生鮮超市(四)    生鮮超市(五)    生鮮超市(六)   

生鮮超市(七)    生鮮超市(八)    生鮮超市(九)   

生鮮超市(十)    生鮮超市(十一)    生鮮超市(十二)    生鮮超市(十三)   

代碼下載

github

教程

學習自慕課網-前端vue結合后端DjangoFramework的在線生鮮超市 

十三、首頁、商品數量、緩存和限速功能開發

 13.1.輪播圖接口實現

首先把pycharm環境改成本地的,vue中local_host也改成本地 

(1)goods/serializer

class BannerSerializer(serializers.ModelSerializer):
    '''
    輪播圖
    '''
    class Meta:
        model = Banner
        fields = "__all__"

(2)goods/views.py

class BannerViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    首頁輪播圖
    """
    queryset = Banner.objects.all().order_by("index")
    serializer_class = BannerSerializer

(3)url

# 配置首頁輪播圖的url
router.register(r'banners', BannerViewset, base_name="banners")

在后台添加首頁輪播圖圖片

 

 13.2.新品接口功能開發

在設計Goods model時候有一個字段is_new

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

實現這個接口只要在goods/filters/GoodsFilter里面添加一個過濾就可以了

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

在后台設置幾個商品 is_new

 

13.3.首頁商品分類顯示功能

 首先是大類,然后里面有

  • 商品商標(多個)
  • 大類下的二級類
  • 廣告商品
  • 所有商品

 

 

(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()
    # 在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__"

(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)url

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

 

13.4.商品點擊數和收藏數

(1)點擊數

GoodsListViewSet其中繼承了mixins.RetrieveModelMixin(獲取商品詳情)

源碼

class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

我們只要重寫他的retrieve方法就可以了

goods/views

  #商品點擊數 + 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)
class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
    '''
    list:
        商品列表,分頁,搜索,過濾,排序
    retrieve:
        獲取商品詳情
    '''

    # authentication_classes = (TokenAuthentication,)
    #這里必須要定義一個默認的排序,否則會報錯
    queryset = Goods.objects.all().order_by('id')
    # 分頁
    pagination_class = GoodsPagination
    #序列化
    serializer_class = GoodsSerializer
    filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)

    # 設置filter的類為我們自定義的類
    #過濾
    filter_class = GoodsFilter
    #搜索
    search_fields = ('name', 'goods_brief', 'goods_desc')
    #排序
    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)
GoodListViewSet

 

(2)收藏數

前面已經寫了UserFavViewset,其中繼承了mixins.CreateModelMixin,添加收藏實際就是創建數據庫

這里重寫它的perform_create方法就可以了

user_operation/view.py

# 用戶收藏的商品數量+1
    def perform_create(self, serializer):
        instance = serializer.save()
        # 這里instance相當於UserFav model,通過它找到goods
        goods = instance.goods
        goods.fav_num += 1
        goods.save()
class UserFavViewset(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin):
    '''
    用戶收藏
    '''
    #permission是用來做權限判斷的
    # IsAuthenticated:必須登錄用戶;IsOwnerOrReadOnly:必須是當前登錄的用戶
    permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)
    #auth使用來做用戶認證的
    authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication)
    #搜索的字段
    lookup_field = 'goods_id'

    #動態選擇serializer
    def get_serializer_class(self):
        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)

    # 用戶收藏的商品數量+1
    def perform_create(self, serializer):
        instance = serializer.save()
        # 這里instance相當於UserFav model,通過它找到goods
        goods = instance.goods
        goods.fav_num += 1
        goods.save()
UserFavViewset

 

(3)用信號量實現

delete和create的時候django model都會發送一個信號量出來,用信號量的方式代碼分離性更好

收藏數+1和-1

(1)user_operation/signal.py

# users_operation/signals.py

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

# post_save:接收信號的方式
#sender: 接收信號的model
@receiver(post_save, sender=UserFav)
def create_UserFav(sender, instance=None, created=False, **kwargs):
    # 是否新建,因為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):
        goods = instance.goods
        goods.fav_num -= 1
        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

 

13.5.商品庫存和銷量修改

庫存數量

商品庫存數量的行為:

  • 新增商品到購物車
  • 修改購物車數量
  • 刪除購物車記錄

trade/views.py

# 庫存數-1
    def perform_create(self, serializer):
        shop_cart = serializer.save()
        goods = shop_cart.goods
        goods.goods_num -= shop_cart.nums
        goods.save()

    # 庫存數+1
    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()
class ShoppingCartViewset(viewsets.ModelViewSet):
    """
    購物車功能
    list:
        獲取購物車詳情
    create:
        加入購物車
    delete:
        刪除購物記錄
    """
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    serializer_class = ShopCartSerializer
    #商品的id
    lookup_field = "goods_id"

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

    #獲取購物車列表
    def get_queryset(self):
        return ShoppingCart.objects.filter(user=self.request.user)

    # 庫存數-1
    def perform_create(self, serializer):
        shop_cart = serializer.save()
        goods = shop_cart.goods
        goods.goods_num -= shop_cart.nums
        goods.save()

    # 庫存數+1
    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()
ShoppingCartViewset

 

商品銷量

商品的銷量只有在支付成功后才會 +1

trade/views.py

                for order_good in order_goods:
                    goods = order_good.goods
                    goods.sold_num += order_good.goods_num
                    goods.save()
class AlipayView(APIView):
    def get(self, request):
        """
        處理支付寶的return_url返回
        """
        processed_dict = {}
        # 1. 獲取GET中參數
        for key, value in request.GET.items():
            processed_dict[key] = value
        # 2. 取出sign
        sign = processed_dict.pop("sign", None)

        # 3. 生成ALipay對象
        alipay = AliPay(
            appid="2016091500517456",
            app_notify_url="http://47.93.198.159:8000/alipay/return/",
            app_private_key_path=private_key_path,
            alipay_public_key_path=ali_pub_key_path,  # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你自己的公鑰,
            debug=True,  # 默認False,
            return_url="http://47.93.198.159:8000/alipay/return/"
        )

        verify_re = alipay.verify(processed_dict, sign)

        # 這里可以不做操作。因為不管發不發return url。notify url都會修改訂單狀態。
        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:
                existed_order.pay_status = trade_status
                existed_order.trade_no = trade_no
                existed_order.pay_time = datetime.now()
                existed_order.save()

            response = redirect("/index/#/app/home/member/order")
            return response

        else:
            response = redirect("index")
            return response

    def post(self, request):
        """
        處理支付寶的notify_url
        """
        #存放post里面所有的數據
        processed_dict = {}
        #取出post里面的數據
        for key, value in request.POST.items():
            processed_dict[key] = value
        #把signpop掉,文檔有說明
        sign = processed_dict.pop("sign", None)

        #生成一個Alipay對象
        alipay = AliPay(
            appid="2016091500517456",
            app_notify_url="http://47.93.198.159:8000/alipay/return/",
            app_private_key_path=private_key_path,
            alipay_public_key_path=ali_pub_key_path,  # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你自己的公鑰,
            debug=True,  # 默認False,
            return_url="http://47.93.198.159: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()
            #需要返回一個'success'給支付寶,如果不返回,支付寶會一直發送訂單支付成功的消息
            return Response("success")
AlipayView

 

13.6.drf的緩存設置

為了加速網站的訪問速度,將一些數據放到緩存當中,取數據的時候首先去緩存中去,然后再去數據庫中取

我們用drf的一個擴展來實現緩存,github上面的使用說明:http://chibisov.github.io/drf-extensions/docs/#caching

 

 

 

 

 (1)安裝

pip install drf-extensions

(2)使用方法

導入

from rest_framework_extensions.cache.mixins import CacheResponseMixin

在GoodsListViewSet中添加緩存功能

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

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

設置過期時間,settings里面

#緩存配置
REST_FRAMEWORK_EXTENSIONS = {
    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 5   #5s過期,時間自己可以隨便設定
}

這個緩存使用的是內存,每次重啟之后就會失效

 

13.7.drf配置redis緩存

使用django-redis第三方庫:http://django-redis-chs.readthedocs.io/zh_CN/latest/#id8    (文檔說明)

 

 (1)安裝

pip install django-redis

(2)settings

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

13.8.drf的throttle設置api的訪問速率

為了防止爬蟲對服務器造成的重大壓力,對數據進行訪問速率限制就顯得非常的重要了

官網使用說明:http://www.django-rest-framework.org/api-guide/throttling/

 

(1)settings中配置

REST_FRAMEWORK = {
    #限速設置
    'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.AnonRateThrottle',   #未登陸用戶
            'rest_framework.throttling.UserRateThrottle'    #登陸用戶
        ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/minute',         #每分鍾可以請求兩次
        'user': '5/minute'          #每分鍾可以請求五次
    }
}

(2)goods/views.py中使用

from rest_framework.throttling import UserRateThrottle,AnonRateThrottle

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

 

 

 

Django REST framework+Vue 打造生鮮超市(十一)

Django REST framework+Vue 打造生鮮超市(十)

Django REST framework+Vue 打造生鮮超市(九)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM