drf除了在數據序列化部分簡寫代碼之外,還在視圖中提供了簡寫操做。因此在django原有的django.views.View類基礎上,drf封裝了多個子類出來提供給咱們使用。html
Django REST framwork 提供的視圖的主要做用:前端
控制序列化器的執行(檢驗、保存、轉換數據)python
控制數據庫查詢的執行數據庫
調用請求類和響應類[這兩個類也是由drf幫咱們再次擴展了一些功能類。django
在接下來的例子中咱們將是在上一篇:Django Rest Framework_序列化器_Serializer
json
繼續引用其數據庫數據已經模型,爲了方便咱們學習,因此先建立一個子應用req,並註冊配置:後端
python manage.py startapp req瀏覽器
REST framework 傳入視圖的request對象再也不是Django默認的HttpRequest對象,而是REST framework提供的擴展了HttpRequest類的Request類的對象。服務器
REST framework 提供了Parser解析器,在接收到請求後會自動根據Content-Type指明的請求數據類型(如JSON、表單等)將請求數據進行parse解析,解析爲類字典[QueryDict]對象保存到Request對象中。app
Request對象的數據是自動根據前端發送數據的格式進行解析以後的結果。
不管前端發送的哪一種格式的數據,咱們均可以以統一的方式讀取數據。
# 1.data
request.data` 返回解析以後的請求體數據。相似於Django中標準的`request.POST`和 request.FILES`屬性,但提供以下特性:
包含了解析以後的文件和非文件數據
包含了對POST、PUT、PATCH請求方式解析後的數據
利用了REST framework的parsers解析器,不只支持表單類型數據,也支持JSON數據
#2.query_params
request.query_params與Django標準的request.GET相同,只是更換了更正確的名稱而已。
rest_framework.response.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會根據前端所需類型數據來設置該參數。
#1.data
傳給response對象的序列化後,但還沒有render處理的數據
status_code
狀態碼的數字
content
通過render處理後的響應數據
(3)狀態碼
爲了方便設置狀態碼,REST framewrok在`rest_framework.status`模塊中提供了經常使用狀態碼常量。
#1.信息告知 - 1xx
#2.成功 - 2xx
#3.重定向 - 3xx
#4.客戶端錯誤 - 4xx
#5.服務器錯誤 - 5xx
Django REST framwork 提供的視圖的主要做用:
控制序列化器的執行(檢驗、保存、轉換數據)
控制數據庫查詢的執行
REST framework 提供了衆多的通用視圖基類與擴展類,以簡化視圖的編寫。
rest_framework.views.APIView
APIView是REST framework提供的全部視圖的基類,繼承自Django的View父類。
APIView與View的不一樣之處在於:
傳入到視圖方法中的是REST framework的Request對象,而不是Django的HttpRequeset對象;
視圖方法能夠返回REST framework的Response對象,視圖會爲響應數據設置(render)符合前端要求的格式;
任何APIException異常都會被捕獲到,而且處理成合適的響應信息;
在進行dispatch()分發前,會對請求進行身份認證、權限檢查、流量控制。
支持定義的類屬性
authentication_classes列表或元祖,身份認證類
permissoin_classes列表或元祖,權限檢查類
throttle_classes列表或元祖,流量控制類
在APIView中仍以常規的類視圖定義方法來實現get() 、post() 或者其餘請求方式的方法。
例:
如今首先咱們將剛建好的名爲req的app寫serializer.py序列器,而後使用以下內容:
from rest_framework import serializers from students.models import Student def check_user(data): if data == "teacher": raise serializers.ValidationError("用戶名不能爲teacher") return data class StudentModelSerializer(serializers.ModelSerializer): class Meta: model = Student # fields = "__all__" # 表示引用全部字段 fields = ["id", "name", "age", "description", "is_18"] # is_18 爲自定製字段,須要在models裏自定義方法。 # exclude = ["age"] # 使用exclude能夠明確排除掉哪些字段, 注意不能和fields同時使用。 # exclude與fields互斥的,不能同事同時使用 # 傳遞額外的參數,爲ModelSerializer添加或修改原有的選項參數 extra_kwargs = { "name": {"max_length": 10, "min_length": 4, "validators": [check_user]}, "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 == "hszTH" and age == 24: raise serializers.ValidationError("hszTH的故事。。。") return attrs
並在req應用的urls.py添加內容爲:
from django.urls import path, re_path from . import views urlpatterns = [ # 區分View 與 APIView path('student1/', views.Student1View.as_view()), path('student2/', views.Student2APIView.as_view()), ]
最後是views.py的內容:
# View from django.views import View from django.http import JsonResponse class Student1View(View): def get(self, request): print(request) # <WSGIRequest: GET '/req/student1/'> data_dict = {"name": "hsz", "age": 24} return JsonResponse(data_dict) # APIView from rest_framework.views import APIView from rest_framework.views import Response from rest_framework import status class Student2APIView(APIView): def get(self, request): print(request) # <rest_framework.request.Request object at 0x7f6f8d1cd7f0> print(request.query_params) # <QueryDict: {}> data_dict = {"name": "hsz", "age": 24} return Response(data_dict, status=status.HTTP_204_NO_CONTENT, headers={"name": "zero"})
經過運行測試:
首先是請求request最後輸出的類型不同,具體見代碼中的解釋。
而後是運行測試截圖:
View獲得:
瀏覽器得:
View post截圖:
APIView截圖:
APIView post截圖:
APIView不只能夠自定義狀態碼,還能夠添加響應頭內容。
在req應用下的urls.py文件添加:
# 使用APIView path("student3/", views.Student3APIView.as_view()), re_path(r"^student3/(?P<pk>\d+)/$", views.Student4APIView.as_view()),
在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 def Student31APIView(APIView): def get(self, request): # get 對應的是數據查詢,查詢全部 student_list = Student.objects.all() serializer = StudentModelSerializer(instance=student_list, many=True) return Response(serializer.data) def post(self, request): # 獲取用戶提交的數據 post 對應的是數據增長 data_dict = request.data serializer = StudentModelSerializer(data=data_dict) # 數據校驗 serializer.is_valid(raise_exception=True) # 數據保存 return Response(serializer.data) # class Student32APIView(APIView): def get(self, request, pk): # 獲取pk值對用的模型對象,須要PK的查詢就是查詢某一條數據 student_obj = Student.objects.get(pk) serializer = StudentModelSerializer(instance=student_obj) return Response(serializer.data) def put(self, request, pk): # 經過pk更新一條數據 student_obj = Student.objects.get(pk) serializer = StudentModelSerializer(instance=student_obj, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def delete(self, request, pk): # 刪除一條數據 Student.objects.filter(pk).delete() return Response(status=status.HTTP_204_NO_CONTENT)
最後能夠實現數據的增刪改查。
測試新增數據:
rest_framework.generics.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
#########################
get_serializer(self, args, *kwargs)
返回序列化器對象,主要用來提供給Mixin擴展類使用,若是咱們在視圖中想要獲取序列化器對象,也能夠直接調用此方法。
提供的關於數據庫查詢的屬性與方法:
屬性:queryset 指明使用的數據查詢集
方法:
get_queryset(self)
返回視圖使用的查詢集,主要用來提供給Mixin擴展類使用,是列表視圖與詳情視圖獲取數據的基礎,默認返回queryset屬性,能夠重寫,例如:
def get_queryset(self):
user = self.request.user
return user.accounts.all()
get_object(self)
返回詳情視圖所需的模型類數據對象,主要用來提供給Mixin擴展類使用。
在試圖中能夠調用該方法獲取詳情信息的模型類對象。
其餘能夠設置的屬性
pagination_class 指明分頁控制類
filter_backends 指明過濾控制後端
簡單介紹了一下,下面咱們就經過代碼來感覺一下。
首先在urls.py內容添加:
# 使用GenericAPIView path("student4/", views.Student41GenericAPIView.as_view()), re_path(r"^student4/(?P<pk>\d+)/$", views.Student42GenericAPIView.as_view()),
而後在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 Student41GenericAPIView(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 Student42GenericAPIView(GenericAPIView): # 獲取全部的模型對象集合 queryset = Student.objects.all() # 指定序列化器 serializer_class = StudentModelSerializer def get(self, request, pk): # 參數pk名,必需要叫pk,不然會報錯 student_obj = self.get_object() serializer = self.get_serializer(instance=student_obj) return Response(serializer.data) def put(self, request, pk): student_obj = self.get_object() serializer = self.get_serializer(instance=student_obj, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def delete(self, request, pk): student_obj = self.get_object() student_obj.delete() return Response(status=status.HTTP_204_NO_CONTENT)
對GenericAPIView這種方式進程測試:
更新數據測試:
數據被成功修改:
使用GenericAPIView結合視圖Mixin擴展類,快速實現數據接口的APIView
ListModelMixin 實現查詢全部數據功能
CreateModelMixin 實現添加數據的功能
RetrieveModelMixin 實現查詢一條數據功能
UpdateModelMixin 更新一條數據的功能
DestroyModelMixin 刪除一條數據的功能
應用urls.py內容新加爲:
# 使用GenericAPIView 結合mixin的擴展類 實現接口 path("student5/", views.Student51MixinGenericAPIView.as_view()), re_path(r"^student5/(?P<pk>\d+)/$", views.Student52MixinGenericAPIView.as_view()),
views.py新增的內容爲:
""" 使用GenericAPIView結合視圖Mixin擴展類,快速實現數據接口的APIView ListModelMixin 實現查詢全部數據功能 CreateModelMixin 實現添加數據的功能 RetrieveModelMixin 實現查詢一條數據功能 UpdateModelMixin 更新一條數據的功能 DestroyModelMixin 刪除一條數據的功能 """ from rest_framework.mixins import ListModelMixin from rest_framework.mixins import CreateModelMixin class Student51MixinGenericAPIView(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() """ RetrieveModelMixin 實現查詢一條數據功能 UpdateModelMixin 更新一條數據的功能 DestroyModelMixin 刪除一條數據的功能 """ from rest_framework.mixins import RetrieveModelMixin from rest_framework.mixins import UpdateModelMixin from rest_framework.mixins import DestroyModelMixin class Student52MixinGenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): # 獲取全部的模型對象集合 queryset = Student.objects.all() # 指定序列化器 serializer_class = StudentModelSerializer def get(self,request,pk): return self.retrieve(request) def put(self,reuqest,pk): return self.update(reuqest) def delete(self,request,pk): return self.destroy(request) # 不要使用delete方法
刪除id=7的數據:
從數據庫查看id爲7的數據被刪除:
應用urls.py內容新加爲:
# 使用內置的擴展子類,生成API接口 path("student6/", views.Student61ListGenericAPIView.as_view()), re_path("^student6/(?P<pk>\d+)/$", views.Student62RUDGenericAPIView.as_view()),
應用views.py添加的內容爲:
""" ListAPIView 獲取全部數據 CreateAPIView 添加數據 """ from rest_framework.generics import ListAPIView, CreateAPIView class Student61ListGenericAPIView(ListAPIView, CreateAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer """ RetrieveAPIView 獲取一條數據 UpdateAPIView 更新一條數據 DestorAPIView 刪除一條數據 RetrieveUpdateDestoryAPIView 上面三個的縮寫 """ # from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView from rest_framework.generics import RetrieveUpdateDestroyAPIView # class Student62RUDGenericAPIView(RetrieveAPIView, UpdateAPIView, DestroyAPIView): class Student62RUDGenericAPIView(RetrieveUpdateDestroyAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer
測試id=3查找數據:
測試結果能夠查詢到數據:
上面5個接口使用了8行代碼生成,可是咱們能夠發現有一半的代碼重複了
因此,咱們要把這些重複的代碼進行整合,可是依靠原來的類視圖,其實有2方面產生衝突的
1. 查詢全部數據、添加數據是不須要聲明pk的,而其餘的接口須要 [路由衝突]
2. 查詢全部數據和查詢一條數據,都是屬於get請求 [請求方法衝突]
爲了解決上面的2個問題,因此DRF提供了視圖集來解決這個問題
須要再urls.py添加下面內容:
# 視圖集的使用 path("student7/", views.Student7ModelViewSet.as_view({"get": "list", "post": "create"})), re_path("^student7/(?P<pk>\d+)/$", views.Student7ModelViewSet.as_view({"get": "retrieve","put": "update","delete": "destroy"})),
視圖文件views.py添加內容爲:
# 將註釋的取消,將註釋的下一行註釋效果同樣 # from rest_framework.viewsets import GenericViewSet # from rest_framework.mixins import ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, \ # CreateModelMixin from rest_framework.viewsets import ModelViewSet # 將註釋的取消,將註釋的下一行註釋效果同樣 # class Student7ModelViewSet(GenericViewSet,ListModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,CreateModelMixin): class Student7ModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
測試一個查詢全部數據,結果成功:
綜上全部的代碼爲:
app註冊配置:
'req.apps.ReqConfig'
總路由urls.py
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('stu/', include("students.urls")), path('sers/', include("sers.urls")), path('req/', include("req.urls")), ]
使用以前的模型類models.py
from django.db import models # Create your models here. class Student(models.Model): # 模型字段 name = models.CharField(max_length=100, verbose_name="姓名") sex = models.BooleanField(default=1, verbose_name="性別") age = models.IntegerField(verbose_name="年齡") class_null = models.CharField(max_length=5, verbose_name="班級編號") description = models.TextField(max_length=1000, verbose_name="個性簽名") class Meta: db_table = "tb_student" verbose_name = "學生" verbose_name_plural = verbose_name def is_18(self): return "big!" if self.age >=18 else "small!"
序列器serializer.py
from rest_framework import serializers from students.models import Student def check_user(data): if data == "teacher": raise serializers.ValidationError("用戶名不能爲teacher") return data class StudentModelSerializer(serializers.ModelSerializer): class Meta: model = Student # fields = "__all__" # 表示引用全部字段 fields = ["id", "name", "age", "description", "is_18"] # is_18 爲自定製字段,須要在models裏自定義方法。 # exclude = ["age"] # 使用exclude能夠明確排除掉哪些字段, 注意不能和fields同時使用。 # exclude與fields互斥的,不能同事同時使用 # 傳遞額外的參數,爲ModelSerializer添加或修改原有的選項參數 extra_kwargs = { "name": {"max_length": 10, "min_length": 4, "validators": [check_user]}, "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 == "hszTH" and age == 24: raise serializers.ValidationError("hszTH的故事。。。") return attrs
子路由urls.py
from django.urls import path, re_path from . import views urlpatterns = [ path("students/", views.Student2View.as_view()), re_path(r"^students/(?P<pk>\d+)/$", views.Student1View.as_view()), path("student3/", views.Student3View.as_view()), # 反序列化階段(update, 執行的請求方式爲put) re_path(r'^student4/(?P<pk>\d+)/$', views.Student4View.as_view()), # 一個序列化器同時實現序列化和反序列化 path('student5/', views.Student5View.as_view()), # 使用模型類序列化器 path('student6/', views.Student6View.as_view()), ]
以上全部點的視圖代碼:
from django.shortcuts import render # Create your views here. from django.http import JsonResponse from django.views import View from students.models import Student from django.shortcuts import HttpResponse from sers.serializers import StudentSerializer import json class Student1View(View): """使用序列化器進行數據的序列化操做""" """序列化器轉換一條數據[模型轉換成字典]""" def get(self, request, pk): # 接收客戶端傳過來的參數,進行過濾查詢,先查出學生對象 student = Student.objects.get(pk=pk) # 轉換數據類型 # 1.實例化序列化器類 """ StudentSerializer(instance=模型對象或者模型列表,客戶端提交的數據,額外要傳遞到序列化器中使用的數據) """ serializer = StudentSerializer(instance=student) # 2.查看序列化器的轉換結果 print(serializer.data) return JsonResponse(serializer.data) class Student2View(View): """序列化器轉換多條數據[模型轉換成字典]""" def get(self, request): student_list = Student.objects.all() print(student_list) # 序列化器轉換多個數據 # many=True 表示本次序列化器轉換若是有多個模型對象列參數,則必須聲明 Many=True serializer = StudentSerializer(instance=student_list, many=True) print(serializer.data) return JsonResponse(serializer.data, safe=False) from sers.serializers import Student3Serializer class Student3View(View): # 反序列化之數據校驗 def post(self, request): # 對數據進行解碼 data = request.body.decode() # 對數據(用戶提交的數據)進行反序列化 data_dict = json.loads(data) # 調用序列化器進行實例化 serializer = Student3Serializer(data=data_dict) # 進行校驗 # is_valid在執行的時候,會自動前後調用 字段的內置選項,自定義驗證方法,自定義驗證函數 # 調用序列化器中寫好的驗證代碼 # 驗證結果 # raise_exception=True 拋出驗證錯誤信息,並阻止代碼繼續日後運行 serializer.is_valid(raise_exception=True) # 獲取錯誤信息 print(serializer.errors) # 獲取合法的數據信息 print(serializer.validated_data) # save 表示讓序列化器開始執行反序列化代碼。create和update的代碼 serializer.save() # return HttpResponse("OK") return JsonResponse(serializer.validated_data) class Student4View(View): def put(self, request, pk): data = request.body.decode() import json data_dict = json.loads(data) # 經過pk從數據庫中獲得相應的原數據 student_obj = Student.objects.get(pk=pk) print(pk) # 有instance參數,調用save方法,就會調用update方法。 serializer = Student3Serializer(instance=student_obj, data=data_dict) # print(serializer) serializer.is_valid(raise_exception=True) serializer.save() # 觸發序列器中的update方法 return JsonResponse(serializer.validated_data) class Student5View(View): def get(self, request): # 獲取全部數據 student_list = Student.objects.all() serializer = Student3Serializer(instance=student_list, many=True) return JsonResponse(serializer.data, safe=False) def post(self, request): data = request.body.decode() data_dict = json.loads(data) # 調用序列化器進行實例化 serializer = Student3Serializer(data=data_dict) # 進行校驗 serializer.is_valid(raise_exception=True) # 這個爲何要等於instance serializer.save() return JsonResponse(serializer.data) from sers.serializers import StudentModelSerializer class Student6View(View): # 若是是get方法請求數據 def get(self, request): # 獲取全部數據 student_list = Student.objects.all() serializer = StudentModelSerializer(instance=student_list, many=True) return JsonResponse(serializer.data, safe=False) # 若是是post方法新增數據 def post(self, request): data = request.body.decode() data_dict = json.loads(data) serializer = StudentModelSerializer(data=data_dict) serializer.is_valid(raise_exception=True) serializer.save() return JsonResponse(serializer.data)