drf除了在數據序列化部分簡寫代碼以外,還在視圖中提供了簡寫操作。所以在django原有的django.views.View類基礎上,drf封裝了多個子類出來提供給我們使用。
Django REST framwork 提供的視圖的主要作用:
-
控制序列化器的執行(檢驗、保存、轉換數據)
-
控制數據庫查詢的執行
-
調用請求類和響應類(這兩個類也是由drf幫我們再次擴展了一些功能類)。
一. 請求與響應
1. Request
REST framework 傳入視圖的request對象不再是Django默認的HttpRequest對象,而是REST framework提供的擴展了HttpRequest類的Request類的對象。
REST framework 提供了Parser解析器,在接收到請求后會自動根據Content-Type指明的請求數據類型(如JSON、表單等)將請求數據進行parse解析,解析為類字典[QueryDict]對象保存到Request對象中。
Request對象的數據是自動根據前端發送數據的格式進行解析之后的結果。
返回解析之后的請求體數據。類似於Django中標准的 request.POST 和 request.FILES 屬性,但提供如下特性:
-
-
包含了解析之后的文件和非文件數據
-
包含了對POST、PUT、PATCH請求方式解析后的數據
-
利用了REST framework的parsers解析器,不僅支持表單類型數據,也支持JSON數據
-
request.query_params與Django標准的 request.GET 相同,只是更換了更正確的名稱而已。
2. Response
from rest_framework.response import Response
REST framework提供了一個響應類Response,使用該類構造響應對象時,響應的具體數據內容會被轉換(render渲染)成符合前端需求的類型。
REST framework提供了 Renderer 渲染器,用來根據請求頭中的Accept(接收數據類型聲明)來自動轉換響應數據到對應格式。如果前端請求中未進行Accept聲明,則會采用默認方式處理響應數據,我們可以通過配置來修改默認響應格式。
可以在rest_framework.settings查找所有的drf默認配置項
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默認響應渲染類 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 瀏覽API渲染器 ) }
構造方式:
Response(data, status=None, template_name=None, headers=None, content_type=None)
data數據不要是render處理之后的數據,只需傳遞python的內建類型數據即可,REST framework會使用 renderer 渲染器處理data。
data不能是復雜結構的數據,如Django的模型類對象,對於這樣的數據我們可以使用 Serializer 序列化器序列化處理后(轉為了Python字典類型)再傳遞給data參數。
參數說明:
-
data: 為響應准備的序列化處理后的數據;
-
status: 狀態碼,默認200;
-
template_name: 模板名稱,如果使用HTMLRenderer 時需指明;
-
headers: 用於存放響應頭信息的字典;
-
content_type: 響應數據的Content-Type,通常此參數無需傳遞,REST framework會根據前端所需類型數據來設置該參數。
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
3.2 成功---2XX
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
3.3 重定向---3XX
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
3.4 客戶端錯誤---4XX
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
3.5 服務器錯誤---5XX
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
二. 視圖
REST framework 提供了眾多的通用視圖基類與擴展類,以簡化視圖的編寫。
Django REST framwork 提供的視圖的主要作用:
-
-
控制數據庫查詢的執行
下面我們介紹一下兩個視圖基類
from rest_framework.views import APIView
APIView 是REST framework提供的所有視圖的基類,繼承自Django的View父類。
APIView 與View的不同之處在於:
-
傳入到視圖方法中的是REST framework的request對象,而不是Django的HttpRequest對象;
-
視圖方法可以返回REST framework的Response對象
-
任何APIException異常都會被捕獲到,並且處理成合適的響應信息;
-
在進行dispatch()分發前,會對請求進行身份認證、權限檢查、流量控制。
支持定義的類屬性
-
authentication_classes 列表或元祖,身份認證類
-
permissoin_classes 列表或元祖,權限檢查類
-
throttle_classes 列表或元祖,流量控制類
在APIView中仍以常規的類視圖定義方法來實現get() 、post() 或者其他請求方式的方法。
2.GenericAPIView(通用視圖類)
from rest_framework.generics import GenericAPIView
繼承自APIView,主要增加了操作序列化器和數據庫查詢的方法,作用是為下面Mixin擴展類的執行提供方法支持。通常在使用時,可搭配一個或多個Mixin擴展類。
提供的關於序列化器使用的屬性與方法
-
-
serializer_class 指明視圖使用的序列化器
-
-
方法:
-
get_serializer_class(self)
當出現一個視圖類中調用多個序列化器時,那么可以通過條件判斷在get_serializer_class方法中通過返回不同的序列化器類名就可以讓視圖方法執行不同的序列化器對象了。
返回序列化器類,默認返回serializer_class,可以重寫,例如:
def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerializer
返回序列化器對象,主要用來提供給Mixin擴展類使用,如果我們在視圖中想要獲取序列化器對象,也可以直接調用此方法。
提供的關於數據庫查詢的屬性與方法
-
屬性:
-
queryset 指明使用的數據查詢集
-
-
-
get_queryset(self)
返回視圖使用的查詢集,主要用來提供給Mixin擴展類使用,是列表視圖與詳情視圖獲取數據的基礎,默認返回queryset屬性,可以重寫,例如:
-
def get_queryset(self): user = self.request.user return user.accounts.all()
返回詳情視圖所需的模型類數據對象,主要用來提供給Mixin擴展類使用。
在試圖中可以調用該方法獲取詳情信息的模型類對象。
其他可以設置的屬性
-
-
pagination_class 指明分頁控制類
-
filter_backends 指明過濾控制后端
-
簡單介紹了一下,下面我們就通過代碼來感受一下。
首先創建新的應用
python3 manage.py startapp req
下面所用到的序列化器類:
from students.models import Student from rest_framework import serializers class StudentModelSerializer(serializers.ModelSerializer): class Meta: model = Student fields = ["id", "name", "age", "sex"] extra_kwargs = { "name": {"max_length": 10, "min_length": 4}, "age": {"max_value": 150, "min_value": 0}, } def validate_name(self, data): if data == "root": raise serializers.ValidationError("用戶名不能為root!") return data def validate(self, attrs): name = attrs.get('name') age = attrs.get('age') if name == "alex" and age == 22: raise serializers.ValidationError("alex在22時的故事。。。") return attrs
3. View與APIView
在req應用下的urls.py文件:
urlpatterns = [ # View與APIView的區別 path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), ]
在req應用下的views.py文件:
""" 測試代碼:區分django的 View 和 drf的 APIView """ from django.views import View from django.http import JsonResponse class Student1View(View): def get(self, request): print(request) # 這是django提供的HttpRequest類 print(request.GET) """打印效果: <WSGIRequest: GET '/req/student1/'> """ data_dict = {'name': "alex", "age": 18} return JsonResponse(data_dict) from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class Student2APIView(APIView): def get(self, request): print(request) # rest_framework擴展后的request print(request.query_params) print(request.data) """打印效果 <rest_framework.request.Request object at 0x00000000054D8898> """ data_dict = {'name': "alex", "age": 18} return Response(data_dict, status=status.HTTP_204_NO_CONTENT, headers={"name": "xiaobai"})
4. APIView實現接口
在req應用下的urls.py文件:
urlpatterns = [ # View與APIView的區別 path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), # 使用APIView path("student3/", views.Student3APIView.as_view()), re_path(r"^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()), ]
在req應用下的views.py文件:
""" 使用APIView提供學生信息的5個API接口 GET /req/student3/ # 獲取全部數據 POST /req/student3/ # 添加數據 GET /req/student3/(?P<pk>\d+) # 獲取一條數據 PUT /req/student3/(?P<pk>\d+) # 更新一條數據 DELETE /req/student3/(?P<pk>\d+) # 刪除一條數據 """ from students.models import Student from req.serializers import StudentModelSerializer class Student3APIView(APIView): def get(self, request): """獲取所有數據""" student_list = Student.objects.all() # 序列化操作 serializer = StudentModelSerializer(instance=student_list, many=True) return Response(serializer.data) def post(self, request): # 獲取用戶提交的數據 data_dict = request.data # 實例化序列化器對象 serializer = StudentModelSerializer(data=data_dict) # 數據校驗 serializer.is_valid(raise_exception=True) # 保存數據 serializer.save() return Response(serializer.validated_data) class Student4APIView(APIView): def get(self, request, pk): # 過濾pk對應的學生對象 student_obj = Student.objects.get(pk=pk) serializer = StudentModelSerializer(instance=student_obj) return Response(serializer.data) def put(self, request, pk): # 過濾pk對應的學生對象 student_obj = Student.objects.get(pk=pk) # 獲取用戶提交的數據 data_dict = request.data serializer = StudentModelSerializer(instance=student_obj, data=data_dict) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.validated_data) def delete(self, request, pk): Student.objects.filter(pk=pk).delete() return Response(status=status.HTTP_204_NO_CONTENT)
5. GenericAPIView實現接口
在req應用下的urls.py文件:
urlpatterns = [ # View與APIView的區別 path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), # 使用APIView path("student3/", views.Student3APIView.as_view()), re_path(r"^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()), # 使用GenericAPIView path("student4/", views.Student5GenericAPIView.as_view()), re_path(r"^student4/(?P<pk>\d+)/$", views.Student6GenericAPIView.as_view()), ]
在req應用下的views.py文件:
""" 使用GenericAPIView提供學生信息的5個API接口 GET /req/student4/ # 獲取全部數據 POST /req/student4/ # 添加數據 GET /req/student4/(?P<pk>\d+) # 獲取一條數據 PUT /req/student4/(?P<pk>\d+) # 更新一條數據 DELETE /req/student4/(?P<pk>\d+) # 刪除一條數據 """ from rest_framework.generics import GenericAPIView class Student5GenericAPIView(GenericAPIView): # 當前視圖類中操作的公共數據,先從數據庫查詢出來 queryset = Student.objects.all() # 設置類視圖中所有方法共有調用的序列化器類 serializer_class = StudentModelSerializer def get(self, request): # 獲取模型數據 student_list = self.get_queryset() # 調用序列化器 serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def post(self, request): """新增數據""" # 獲取用戶提交的數據並實例化序列化器對象 serializer = self.get_serializer(data=request.data) # 數據校驗 serializer.is_valid(raise_exception=True) # 保存數據 serializer.save() return Response(serializer.data) class Student6GenericAPIView(GenericAPIView): # 當前視圖類中操作的公共數據,先從數據庫查詢出來 queryset = Student.objects.all() # 設置類視圖中所有方法共有調用的序列化器類 serializer_class = StudentModelSerializer def get(self, request, pk): """參數pk名,必須要叫pk,否則會報錯。""" # 獲取模型對象 instance = self.get_object() serializer = self.get_serializer(instance=instance) return Response(serializer.data) def put(self, request, pk): instance = self.get_object() serializer = self.get_serializer(instance=instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def delete(self, request, pk): # 獲取模型對象 instance = self.get_object() # 刪除模型對象 instance.delete() return Response(status=status.HTTP_204_NO_CONTENT)
6. 五個視圖擴展類
""" 使用GenericAPIView結合視圖Mixin擴展類,快速實現數據接口的APIView ListModelMixin 實現查詢所有數據功能 CreateModelMixin 實現添加數據的功能 RetrieveModelMixin 實現查詢一條數據功能 UpdateModelMixin 更新一條數據的功能 DestroyModelMixin 刪除一條數據的功能 """
在req應用下的urls.py文件:
urlpatterns = [ # View與APIView的區別 path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), # 使用APIView path("student3/", views.Student3APIView.as_view()), re_path(r"^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()), # 使用GenericAPIView path("student4/", views.Student5GenericAPIView.as_view()), re_path(r"^student4/(?P<pk>\d+)/$", views.Student6GenericAPIView.as_view()), # 使用GenericAPIView,結合Mixin的擴展類 path("student5/", views.Student7GenericAPIView.as_view()), re_path(r"^student5/(?P<pk>\d+)/$", views.Student8GenericAPIView.as_view()), ]
在req應用下的views.py文件:
from rest_framework.mixins import ListModelMixin, CreateModelMixin class Student7GenericAPIView(GenericAPIView, ListModelMixin, CreateModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get(self, request): return self.list(request) def post(self, request): return self.create(request) from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin class Student8GenericAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get(self, request, pk): return self.retrieve(request) def put(self, request, pk): return self.update(request) def delete(self, request, pk): return self.destroy(request)
7. 內置的擴展子類
在req應用下的urls.py文件:
urlpatterns = [ # View與APIView的區別 path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), # 使用APIView path("student3/", views.Student3APIView.as_view()), re_path(r"^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()), # 使用GenericAPIView path("student4/", views.Student5GenericAPIView.as_view()), re_path(r"^student4/(?P<pk>\d+)/$", views.Student6GenericAPIView.as_view()), # 使用GenericAPIView,結合Mixin的擴展類 path("student5/", views.Student7GenericAPIView.as_view()), re_path(r"^student5/(?P<pk>\d+)/$", views.Student8GenericAPIView.as_view()), # 使用內置的擴展子類,生成API接口 path("student6/", views.Student9GenericAPIView.as_view()), re_path(r"^student6/(?P<pk>\d+)/$", views.Student10GenericAPIView.as_view()), ]
在req應用下的views.py文件:
""" DRF里面,內置了一些同時繼承了GenericAPIView和Mixins擴展類的視圖子類, 我們可以直接繼承這些子類就可以生成對應的API接口 """ """ ListAPIView 獲取所有數據 CreateAPIView 添加數據 """ from rest_framework.generics import ListAPIView, CreateAPIView class Student9GenericAPIView(ListAPIView, CreateAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer """ RetrieveAPIView 獲取一條數據 UpdateAPIView 更新一條數據 DestorAPIView 刪除一條數據 RetrieveUpdateDestoryAPIView 上面三個的縮寫 """ from rest_framework.generics import RetrieveUpdateDestroyAPIView class Student10GenericAPIView(RetrieveUpdateDestroyAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer
8. 視圖集
在req應用下的urls.py文件:
urlpatterns = [ # View與APIView的區別 path("student1/", views.Student1View.as_view()), path("student2/", views.Student2APIView.as_view()), # 使用APIView path("student3/", views.Student3APIView.as_view()), re_path(r"^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()), # 使用GenericAPIView path("student4/", views.Student5GenericAPIView.as_view()), re_path(r"^student4/(?P<pk>\d+)/$", views.Student6GenericAPIView.as_view()), # 使用GenericAPIView,結合Mixin的擴展類 path("student5/", views.Student7GenericAPIView.as_view()), re_path(r"^student5/(?P<pk>\d+)/$", views.Student8GenericAPIView.as_view()), # 使用內置的擴展子類,生成API接口 path("student6/", views.Student9GenericAPIView.as_view()), re_path(r"^student6/(?P<pk>\d+)/$", views.Student10GenericAPIView.as_view()), # 視圖集 path("student7/", views.Student11GenericAPIView.as_view({"get": "list", "post": "create"})), re_path(r"^student7/(?P<pk>\d+)/$", views.Student11GenericAPIView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), ]
在req應用下的views.py文件:
""" 視圖集 上面5個接口使用了8行代碼生成,但是我們可以發現有一半的代碼重復了 所以,我們要把這些重復的代碼進行整合,但是依靠原來的類視圖,其實有2方面產生沖突的 1. 查詢所有數據、添加數據是不需要聲明pk的,而其他的接口需要 [路由沖突了] 2. 查詢所有數據和查詢一條數據,都是屬於get請求 [請求方法沖突了] 為了解決上面的2個問題,所以DRF提供了視圖集來解決這個問題 """ from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin # 這兩個是等價的 # class Student11GenericAPIView(GenericViewSet, ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin): class Student11GenericAPIView(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer