目錄
生鮮超市(十) 生鮮超市(十一) 生鮮超市(十二) 生鮮超市(十三)
代碼下載
教程
學習自慕課網-前端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)
(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()
(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()
商品銷量
商品的銷量只有在支付成功后才會 +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")
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 打造生鮮超市(十一)