Django REST framework+Vue 打造生鮮電商項目(筆記二)
(轉自https://www.cnblogs.com/derek1184405959/p/8768059.html)(有修改)
接下來開始引入django resfulframework,體現它的強大之處
一、首先,按照以前的做法,如果我們想把商品列表打包成JSON數據顯示在首頁,就是:
1、寫view函數
# goods/view.py from django.views.generic import View from goods.models import Goods class GoodsListView(View): def get(self,request): #通過django的view實現商品列表頁json_list = [] #獲取所有商品 goods = Goods.objects.all() for good in goods: json_dict = {} #獲取商品的每個字段,鍵值對形式 json_dict[‘name‘] = good.name json_dict[‘category‘] = good.category.name json_dict[‘market_price‘] = good.market_price json_list.append(json_dict)from django.http import HttpResponse import json #返回json,一定要指定類型content_type=‘application/json‘ return HttpResponse(json.dumps(json_list),content_type=‘application/json‘)
2、配置url
from goods.view import GoodsListView urlpatterns = [ #商品列表頁 path(‘goods/‘,GoodsListView.as_view(),name=‘goods-list‘) ]
訪問http://127.0.0.1:8000/goods/ 可以獲取商品列表信息的json數據
二、django的serializer序列化model
當字段比較多時,一個字段一個字段的提取很麻煩,可以用model_to_dict,將model整個轉化為dict
# goods/view.py from django.views.generic import View from goods.models import Goods class GoodsListView(View): def get(self,request): #通過django的view實現商品列表頁 json_list = [] #獲取所有商品 goods = Goods.objects.all() from django.forms.models import model_to_dict for good in goods: json_dict = model_to_dict(good) json_list.append(json_dict) from django.http import HttpResponse import json #返回json,一定要指定類型content_type=‘application/json‘ return HttpResponse(json.dumps(json_list),content_type=‘application/json‘)
但是這樣有個問題,就是ImageFieldFile 和add_time字段不能序列化
這時就要用到django的serializers
# goods/view_base.py from django.views.generic import View from goods.models import Goods class GoodsListView(View): def get(self,request): #通過django的view實現商品列表頁 json_list = [] #獲取所有商品 goods = Goods.objects.all() import json from django.core import serializers from django.http import JsonResponse json_data = serializers.serialize(‘json‘,goods) json_data = json.loads(json_data) #In order to allow non-dict objects to be serialized set the safe parameter to False. return JsonResponse(json_data,safe=False) # 註意,這裏用的JsonResponse,這樣就不用json.dumps了
註意,這裏的圖片路徑是相對路徑。還有其中的goods_desc之所以會那樣是因為我們後臺在添加數據用的是富文本,不是文本框,因此可以上傳圖片啥的,保存起來就是這個效果。
django的serializer雖然可以很簡單實現序列化,但是有幾個缺點
(1)字段序列化定死的,要想重組的話非常麻煩
(2)從上面截圖可以看出來,images保存的是一個相對路徑,我們還需要補全路徑,而這些drf都可以幫助我們做到
以上寫了這麽多只是為了引入django rest framework和簡單介紹django的序列化用法,下面就是重點講解django rest framework了
三、3種方式實現商品列表頁
1、安裝djangorestframework、coreapi(drf的文檔支持)、django-guardian(drf對象級別的權限支持)
2、配置def文檔的url
# urls.py from rest_framework.documentation import include_docs_urls urlpatterns = [ #drf文檔,title自定義 path(‘docs‘,include_docs_urls(title=‘仙劍奇俠傳‘)), ]
3、配置rest_framework
#setting.py INSTALLED_APPS = [ ‘rest_framework‘, ]
# urls.py urlpatterns = [ path(‘api-auth/‘,include(‘rest_framework.urls‘)), ]
4、第一種方法:APIview方式實現商品列表頁
(1)goods文件夾下面新建serializers.py
用drf的序列化實現商品列表頁展示,代碼如下:
# goods/serializers.py from rest_framework import serializers class GoodsSerializer(serializers.Serializer): name = serializers.CharField(required=True,max_length=100) click_num = serializers.IntegerField(default=0) goods_front_image = serializers.ImageField()
這裏的serializers.py可以理解成Django中在建立驗證表單Form,聯系這個知識點可以有助於理解。
(2)goods/views.py
# googd/views.py from rest_framework.views import APIView from goods.serializers import GoodsSerializer from .models import Goods from rest_framework.response import Response class GoodsListView(APIView): ‘‘‘ 商品列表 ‘‘‘ def get(self,request,format=None): goods = Goods.objects.all() goods_serialzer = GoodsSerializer(goods,many=True) return Response(goods_serialzer.data)
(3)drf的Modelserializer實現商品列表頁
上面是用Serializer實現的,需要自己手動添加字段,如果用Modelserializer,會更加的方便,直接用__all__就可以全部序列化
# goods/serializers.py from rest_framework import serializers from .models import Goods #Serializer實現商品列表頁 # class GoodsSerializer(serializers.Serializer): # name = serializers.CharField(required=True,max_length=100) # click_num = serializers.IntegerField(default=0) # goods_front_image = serializers.ImageField() #ModelSerializer實現商品列表頁 class GoodsSerializer(serializers.ModelSerializer): class Meta: model = Goods fields = ‘__all__‘
外鍵category只顯示分類的id,如果我們也要把外鍵對應的內容也顯示出來。用serializers,它還可以嵌套使用,覆蓋外鍵字段
# goods/serializers.py from rest_framework import serializers from .models import Goods,GoodsCategory #Serializer實現商品列表頁 # class GoodsSerializer(serializers.Serializer): # name = serializers.CharField(required=True,max_length=100) # click_num = serializers.IntegerField(default=0) # goods_front_image = serializers.ImageField() class CategorySerializer(serializers.ModelSerializer): class Meta: model = GoodsCategory fields = "__all__" #ModelSerializer實現商品列表頁 class GoodsSerializer(serializers.ModelSerializer): #覆蓋外鍵字段 category = CategorySerializer() class Meta: model = Goods fields = ‘__all__‘
5、第二種方法:GenericView實現商品列表頁
(1)mixins和generic一起使用
generic裏面的GenericAPIView繼承APIView,封裝了很多方法,比APIView功能更強大
用的時候需要定義queryset和serializer_class
GenericAPIView裏面默認為空
queryset = None
serializer_class = None
mixins裏的ListModelMixin裏面list方法幫我們做好了分頁和序列化的工作,只要調用就好了
實現如下:
from goods.serializers import GoodsSerializer from .models import Goods from rest_framework.response import Response from rest_framework import mixins from rest_framework import generics class GoodsListView(mixins.ListModelMixin,generics.GenericAPIView): ‘商品列表頁‘ queryset = Goods.objects.all() serializer_class = GoodsSerializer def get(self,request,*args,**kwargs): return self.list(request,*args,**kwargs)
更進一步的優化:
可以直接繼承ListAPIView,ListAPIView主要做了兩件事:
(1)ListAPIView(mixins.ListModelMixin,GenericAPIView) #繼承了這兩個類
(2)寫好了get方法
這下,我們要獲取商品列表頁的信息,只要寫三行代碼就可以了
class GoodsListView(generics.ListAPIView): ‘商品列表頁‘ queryset = Goods.objects.all() serializer_class = GoodsSerializer
6、第三種方法:viewsets和router完成商品列表頁
主要用到viewsets中的GenericViewSet
ViewSets和Routers結合使用
因為通過GenericViewSet中的viewSetMixin,我們不需要通過在views.py中寫函數來進行綁定,在router的幫助下,直接在urls.py中配置的時候來進行綁定,如下所示:
# urls.py from goods.views import GoodsListViewSet from rest_framework.routers import DefaultRouter router = DefaultRouter() #配置goods的url router.register(r‘goods‘, GoodsListViewSet) urlpatterns = [ #商品列表頁 re_path(‘^‘, include(router.urls)), ]
# views.py class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): ‘商品列表頁‘ # 分頁 pagination_class = GoodsPagination #這裏必須要定義一個默認的排序,否則會報錯 queryset = Goods.objects.all().order_by(‘id‘) serializer_class = GoodsSerializer
如果不想用router的話,可改為:
# urls.py goods_list = GoodsListViewSet.as_view( {‘get‘: ‘list‘,} )
7、drf的APIView、GenericView、viewsets和router的原理分析
genericViewSet 是最高的一層
往下
GenericViewSet(viewsets) ----drf
GenericAPIView ---drf
APIView ---drf
View ----django
這些view功能的不同,主要的是有mixin的存在
mixins總共有五種:
CreateModelMixin
ListModelMixin
UpdateModelMixin
RetrieveModelMixin
DestoryModelMixin
以ListModelMixin為例:
如果不繼承ListModelMixin的話,就無法將get和商品的列表關聯起來,另外還有其中的分頁等等,都無法實現。
還有其它幾個mixin(增刪改查局部),這些功能都是mixin做的
我們一般都是用viewsets
ViewSet類與View類其實幾乎是相同的,但提供的是read或update這些操作,而不是get或put 等HTTP動作。同時,ViewSet為我們提供了默認的URL結構, 使得我們能更專註於API本身。
Router提供了一種簡單,快速,集成的方式來定義一系列的urls
8、添加分頁功能
先看rest_framework/settings.py源碼,裏面可以找到如何配置:比如認證、權限和分頁等等
添加分頁功能,配置如下:
REST_FRAMEWORK = { #分頁 ‘DEFAULT_PAGINATION_CLASS‘: ‘rest_framework.pagination.PageNumberPagination‘, #每頁顯示的個數 ‘PAGE_SIZE‘: 10, }
自定義分頁功能
from rest_framework.pagination import PageNumberPagination class GoodsPagination(PageNumberPagination): ‘‘‘ 商品列表自定義分頁 ‘‘‘ #默認每頁顯示的個數 page_size = 10 #可以動態改變每頁顯示的個數 page_size_query_param = ‘page_size‘ #頁碼參數 page_query_param = ‘page‘ #最多能顯示多少頁 max_page_size = 100 class GoodsListView(generics.ListAPIView): ‘商品列表頁‘ pagination_class = GoodsPagination #分頁 queryset = Goods.objects.all() serializer_class = GoodsSerializer
settings.py裏面就不用設置了
9、drf的request和response介紹
REST framework 的 Request
類擴展與標準的 HttpRequest
,並做了相應的增強,比如更加靈活的請求解析(request parsing)和認證(request authentication)。
Request 解析
REST framwork 的 Request
對象提供了靈活的請求解析,允許你使用 JSON data 或 其他 media types 像通常處理表單數據一樣處理請求。
.data
request.data
返回請求主題的解析內容。這跟標準的 request.POST
和 request.FILES
類似,並且還具有以下特點:
- 包括所有解析的內容,文件(file) 和 非文件(non-file inputs)。
- 支持解析
POST
以外的 HTTP method , 比如PUT
,PATCH
。 - 更加靈活,不僅僅支持表單數據,傳入同樣的 JSON 數據一樣可以正確解析,並且不用做額外的處理(意思是前端不管提交的是表單數據,還是 JSON 數據,
.data
都能夠正確解析)。
.data 具體操作,以後再說~
.query_params
request.query_params
等同於 request.GET
,不過其名字更加容易理解。
為了代碼更加清晰可讀,推薦使用 request.query_params
,而不是 Django 中的 request.GET
,這樣那夠讓你的代碼更加明顯的體現出 ----- 任何 HTTP method 類型都可能包含查詢參數(query parameters),而不僅僅只是 ‘GET‘ 請求。
.parser
APIView
類或者 @api_view
裝飾器將根據視圖上設置的 parser_classes
或 settings
文件中的 DEFAULT_PARSER_CLASSES
設置來確保此屬性(.parsers
)自動設置為 Parser
實例列表。
通常不需要關註該屬性......
如果你非要看看它裏面是什麽,可以打印出來看看,大概長這樣:
[<rest_framework.parsers.JSONParser object at 0x7fa850202d68>, <rest_framework.parsers.FormParser object at 0x7fa850202be0>, <rest_framework.parsers.MultiPartParser object at 0x7fa850202860>]
包含三個解析器 JSONParser
,FormParser
,MultiPartParser
。
註意: 如果客戶端發送格式錯誤的內容,則訪問
request.data
可能會引發ParseError
。默認情況下, REST framework 的APIView
類或者@api_view
裝飾器將捕獲錯誤並返回400 Bad Request
響應。 如果客戶端發送的請求內容無法解析(不同於格式錯誤),則會引發UnsupportedMediaType
異常,默認情況下會被捕獲並返回415 Unsupported Media Type
響應。
Responses
與基本的 HttpResponse 對象不同,TemplateResponse 對象保留了視圖提供的用於計算響應的上下文的詳細信息。直到需要時才會計算最終的響應輸出,也就是在後面的響應過程中進行計算。 — Django 文檔
REST framework 通過提供一個 Response
類來支持 HTTP 內容協商,該類允許你根據客戶端請求返回不同的表現形式(如: JSON ,HTML 等)。
Response
類的子類是 Django 的 SimpleTemplateResponse
。Response
對象使用數據進行初始化,數據應由 Python 對象(native Python primitives)組成。然後 REST framework 使用標準的 HTTP 內容協商來確定它應該如何渲染最終響應的內容。
當然,您也可以不使用 Response
類,直接返回常規 HttpResponse
或 StreamingHttpResponse
對象。 使用 Response
類只是提供了一個更好的交互方式,它可以返回多種格式。
除非由於某種原因需要大幅度定制 REST framework ,否則應該始終對返回 Response
對象的視圖使用 APIView
類或 @api_view
裝飾器。這樣做可以確保視圖執行內容協商,並在視圖返回之前為響應選擇適當的渲染器。
創建 response
Response()
與普通 HttpResponse
對象不同,您不會使用渲染的內容實例化 Response
對象。相反,您傳遞的是未渲染的數據,可能包含任何 Python 對象。
由於 Response
類使用的渲染器不能處理復雜的數據類型(比如 Django 的模型實例),所以需要在創建 Response
對象之前將數據序列化為基本的數據類型。
你可以使用 REST framework 的 Serializer
類來執行序列化的操作,也可以用自己的方式來序列化。
構造方法: Response(data, status=None, template_name=None, headers=None, content_type=None)
參數:
data
: 響應的序列化數據。status
: 響應的狀態代碼。默認為200。template_name
: 選擇HTMLRenderer
時使用的模板名稱。headers
: 設置 HTTP header,字典類型。content_type
: 響應的內容類型,通常渲染器會根據內容協商的結果自動設置,但有些時候需要手動指定。
屬性
.data
還沒有渲染,但已經序列化的響應數據。
.status_code
狀態碼
.content
將會返回的響應內容,必須先調用 .render()
方法,才能訪問 .content
。
.template_name
只有在 response 的渲染器是 HTMLRenderer
或其他自定義模板渲染器時才需要提供。
.accepted_renderer
用於將會返回的響應內容的渲染器實例。
從視圖返回響應之前由 APIView
或 @api_view
自動設置。
.accepted_media_type
內容協商階段選擇的媒體類型。
從視圖返回響應之前由 APIView
或 @api_view
自動設置。
.renderer_context
將傳遞給渲染器的 .render()
方法的附加的上下文信息字典。
從視圖返回響應之前由 APIView
或 @api_view
自動設置。
標準 HttpResponse 屬性
Response
類擴展於 SimpleTemplateResponse
,並且響應中也提供了所有常用的屬性和方法。例如,您可以用標準方式在響應中設置 header:
response = Response()
response[‘Cache-Control‘] = ‘no-cache‘
.render()
與其他任何 TemplateResponse
一樣,調用此方法將響應的序列化數據呈現為最終響應內容。響應內容將設置為在 accepted_renderer
實例上調用 .render(data,accepted_media_type,renderer_context)
方法的結果。
通常不需要自己調用 .render()
,因為它是由 Django 處理的。
Django REST framework+Vue 打造生鮮電商項目(筆記二)