使用視圖集ViewSet,能夠將一系列邏輯相關的動做放到一個類中:前端
list() 提供一組數據python
retrieve() 提供單個數據數據庫
create() 建立數據django
update() 保存數據api
destory() 刪除數據瀏覽器
ViewSet視圖集類再也不實現get()、post()等方法,而是實現動做 action 如 list() 、create() 等。服務器
視圖集只在使用as_view()方法的時候,纔會將action動做與具體請求方式對應上。session
在此以前首先建立一個新的app應用名爲:collectapp
python3 manage.py startapp collectide
進行相應的註冊以及配置url
1.ViewSet
繼承自APIView 與 ViewSetMixin做用也與APIView基本相似,提供了身份認證、權限校驗、流量管理等。
ViewSet主要經過繼承ViewSetMixin來實如今調用as_view()時傳入字典(如{'get':'list'})的映射處理工做。
在ViewSet中,沒有提供任何動做action方法,須要咱們本身實現action方法。
例1:
collect應用中的urls.py的內容爲:
from django.urls import path, re_path from collect import views urlpatterns = [ # 不要在同一路由的as_view 中寫兩個一樣的http請求,會產生覆蓋!!! # ViewSet path("student1/", views.Student1ViewSet.as_view({"get": "get_5"})), path("student1/get_girl/", views.Student1ViewSet.as_view({"get": "get_5_girl"})), re_path(r"^student1/(?P<pk>\d+)/$", views.Student1ViewSet.as_view({"get": "get_one"})), ]
colectl應用中的views內容:
from students.models import Student from rest_framework.response import Response class Student1ViewSet(ViewSet): def get_5(self, request): # 獲取5條數據 student_list = Student.objects.all()[:5] serializer = StudentModelSerializer(instance=student_list, many=True) # 多個數時候,須要加many=True return Response(serializer.data) def get_one(self, request, pk): # 獲取一條數據 student_obj = Student.objects.get(pk=pk) serializer = StudentModelSerializer(instance=student_obj) return Response(serializer.data)
查詢5條數據:
查詢id=5的數據:
查找性別女的5條數據:
例2:過渡版
collect應用中的urls.py的添加內容:
# ViewSet 過渡版 path("student2/", views.Student2ViewSet.as_view({"get": "get_5"})), path("student2/get_girl/", views.Student2ViewSet.as_view({"get": "get_5_girl"})), re_path(r"^student2/(?P<pk>\d+)/$", views.Student2ViewSet.as_view({"get": "get_one"})),
collect應用中的views添加內容:
"""若是但願在視圖集中調用GenericAPIView的功能,則能夠採用下面方式""" from rest_framework.generics import GenericAPIView class Student2ViewSet(ViewSet, GenericAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_5(self, request): """獲取5條數據""" student_list = self.get_queryset()[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def get_one(self, request, pk): """獲取一條數據""" student_obj = self.get_object() serializer = self.get_serializer(instance=student_obj) return Response(serializer.data) def get_5_girl(self, request): """獲取5條女孩數據""" student_list = self.get_queryset().filter(sex=False)[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data)
測試一個能夠查詢多條數據,其餘就不一一測試。
使用ViewSet一般並不方便,由於list、retrieve、create、update、destory等方法都須要本身編寫,而這些方法與前面講過的Mixin擴展類提供的方法同名,因此咱們能夠經過繼承Mixin擴展類來複用這些方法而無需本身編寫。可是Mixin擴展類依賴與GenericAPIView,因此還須要繼承GenericAPIView。
GenericViewSet就幫助咱們完成了這樣的繼承工做,繼承自GenericAPIView與ViewSetMixin,在實現了調用as_view()時傳入字典(如{'get':'list'})的映射處理工做的同時,還提供了GenericAPIView提供的基礎方法,能夠直接搭配Mixin擴展類使用。
例1:
collect應用中的urls.py的添加內容:
# GenericViewSet path("student3/", views.Student3GenericViewSet.as_view({"get": "get_5"})), path("student3/get_girl/", views.Student3GenericViewSet.as_view({"get": "get_5_girl"})),
collect應用中的views.py添加內容:
上面的方式,雖然實現視圖集中調用GenericAPIView,可是咱們要多了一些類的繼承。
因此咱們能夠直接繼承 GenericViewSet
from rest_framework.viewsets import GenericViewSet class Student3GenericViewSet(GenericViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_5(self, request): # 獲取5條數據 student_list = self.get_queryset()[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def get_5_girl(self, request): # 獲取5條女孩數據 student_list = self.get_queryset().filter(sex=False)[:5] serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data)
進程測試一個查詢5條女孩數據的:
例2:
在使用GenericViewSet時,雖然已經提供了基本調用數據集(queryset)和序列化器屬性,可是咱們要編寫一些基本的
API時,仍是須要調用DRF提供的模型擴展類 [Mixins]
collect應用中的urls.py的添加內容:
# GenericViewSet,能夠和模型類進行組合快速生成基本的API接口 path('student4/', views.Student4GenericViewSetAndMixin.as_view({"get": "list", "post": "create"})),
collect應用中的views.py添加內容:
# 這樣不要試圖寫方法,只要urls中寫對應請求方式便可。
from rest_framework.mixins import ListModelMixin, CreateModelMixin class Student4GenericViewSetAndMixin(GenericViewSet, ListModelMixin, CreateModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer
測試返回全部數據:
繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
不用再寫方法,直接在路由處進行配置寫方法便可。
例:
collect應用中的urls.py的添加內容:
# ModelViewSet 默認提供了5個API接口 path('student5/', views.Student5ModelViewSet.as_view({"get": "list", "post": "create"})), re_path(r"^student5/(?P<pk>\d+)/$", views.Student5ModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
collect應用中的views.py添加內容:
from rest_framework.viewsets import ModelViewSet class Student5ModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
測試刪除id=15的數據:
從數據庫看出數據被刪除:
繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。
例:
collect應用中的urls.py的添加內容:
# ReadOnlyModelViewSet path('student6/', views.Student6ReadOnlyModelViewSet.as_view({'get': 'list'})), re_path(r"^student6/(?P<pk>\d+)/$", views.Student6ReadOnlyModelViewSet.as_view({"get": "retrieve"})),
collect應用中的views.py添加內容:
from rest_framework.viewsets import ReadOnlyModelViewSet class Student6ReadOnlyModelViewSet(ReadOnlyModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
在視圖集中,除了上述默認的方法動做外,那麼有時候會出現一個類中須要調用多個序列化器,那麼能夠進行添加自定義動做。
首先是對serializer.py新增新的序列化器類代碼以下:
class StudentMyModelSerializer(serializers.ModelSerializer): class Meta: model = Student fields = ["id", "name"]
urls.py內添加:
# 一個視圖類調用多個序列化器類 path("student8/", views.Student8GenericAPIView.as_view()),
views.py添加內容爲:
from .serializers import StudentMyModelSerializer class Student8GenericAPIView(GenericAPIView): """原來的視圖類中基本上一個視圖類只會調用一個序列化器,固然也有可能要調用多個序列化器""" class Student8GenericAPIView(GenericAPIView): queryset = Student.objects.all() # GenericAPI內部調用序列化器的方法,咱們能夠重寫這個方法來實現根據不一樣的需求來調用不一樣的序列化器 def get_serializer_class(self): if self.request.method == "GET": return StudentMyModelSerializer # 若是是GET 返回的是新的序列化器類 return StudentModelSerializer def get(self, request): """獲取全部數據的id和name""" student_list = self.get_queryset() serializer = self.get_serializer(instance=student_list, many=True) return Response(serializer.data) def post(self, request): """添加數據""" data = request.data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data)
urls.py內添加:
# 一個視圖集調用多個序列化器類 path("student9/", views.Student9GenericAPIView.as_view({"get": "list"})), re_path(r"^student9/(?P<pk>\d+)/$", views.Student9GenericAPIView.as_view({"get": "retrieve"})),
views.py添加內容爲:
class Student9GenericAPIView(ModelViewSet): queryset = Student.objects.all() """要求: 列表數據list,返回2個字段, 詳情數據retrieve,返回全部字段, """ def get_serializer_class(self): # print(self.action) # 當前請求要執行的方法名稱 if self.action == "list": return StudentMyModelSerializer return StudentModelSerializer
測試獲取所有隻有兩個字段:
查詢單個顯示多個字段:
對於視圖集ViewSet,咱們除了能夠本身手動指明請求方式與動做action之間的對應關係外,還可使用Routers來幫助咱們快速實現路由信息。
REST framework提供了兩個router
SimpleRouter
DefaultRouter
例如:
from rest_framework import routers router = routers.DefaultRouter() router.register(r'router_stu', StudentModelViewSet, base_name='student') register(prefix, viewset, base_name)
prefix 該視圖集的路由前綴
viewset 視圖集
base_name 路由別名的前綴
如上述代碼會造成的路由以下:
^books/$ name: book-list
^books/{pk}/$ name: book-detail
能夠有兩種方式:
urlpatterns = [
...
]
urlpatterns += router.urls
或
urlpatterns = [ ... url(r'^', include(router.urls)) ]
例:urls.py代碼爲:
""" 有了視圖集之後,視圖文件中多個視圖類能夠合併成一個,可是,路由的代碼就變得複雜了, 須要咱們常常在as_view方法 ,編寫http請求和視圖方法的對應關係, 事實上,在路由中,DRF也提供了一個路由類給咱們對路由的代碼進行簡寫。 固然,這個路由類僅針對於 視圖集 纔可使用。 """ from rest_framework.routers import DefaultRouter, SimpleRouter # 實例化router對象 router = DefaultRouter() # 會生成api-root # router = SimpleRouter() # router.register("訪問地址前綴","視圖集類","訪問別買[可選]") # 註冊視圖視圖集類 router.register("student7", views.Student7RouterModelViewSet) # url後綴不要加/ # print(router.urls) # 把路由列表註冊到django項目中 urlpatterns += router.urls
上面的代碼就成功生成了路由地址[增/刪/改/查一條/查多條的功能],可是不會自動咱們在視圖集自定義方法的路由。
因此咱們若是也要給自定義方法生成路由,則須要進行action動做的聲明。
在視圖集中,若是想要讓Router自動幫助咱們爲自定義的動做生成路由信息,須要使用rest_framework.decorators.action裝飾器。
self.action # 獲取本次請求的視圖方法名
以action裝飾器裝飾的方法名會做爲action動做名,與list、retrieve等同。
action裝飾器能夠接收兩個參數:
methods: 聲明該action對應的請求方式,列表傳遞
detail
聲明該action的路徑是否與單一資源對應,及是不是
xxx/<pk>/action方法名/
True 表示路徑格式是`xxx/<pk>/action方法名/`
False 表示路徑格式是`xxx/action方法名/
例:配合添加路由例子中的代碼結合
視圖views.py代碼:
from rest_framework.decorators import action class Student7RouterModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer # methods 指定容許哪些http請求訪問當前視圖方法 # detail 指定生成的路由地址中是否要夾帶pk值,True爲須要 # @action(methods=['get', "post"], detail=False) # def get_6(self, request): # < URLPattern # '^student7/get_6/$'[name = 'student-get-6'] >, # < URLPattern # '^student7/get_6\.(?P<format>[a-z0-9]+)/?$'[name = 'student-get-6'] >, # @action(methods=['get', "post"], detail=True) # 能夠添加多個方法實現一種功能,不過通常不這樣加 @action(methods=['get'], detail=True) def get_6(self, request, pk): # < URLPattern # '^student7/(?P<pk>[^/.]+)/get_6/$'[name = 'student-get-6'] >, # < URLPattern # '^student7/(?P<pk>[^/.]+)/get_6\.(?P<format>[a-z0-9]+)/?$'[name = 'student-get-6'] >, student_obj = self.get_queryset().get(pk=pk) serializer = self.get_serializer(instance=student_obj) return Response(serializer.data)
通過測試能夠進行查詢一條數據,其餘就很少作測試:
DefaultRouter與SimpleRouter的區別是,DefaultRouter會多附帶一個默認的API根視圖,返回一個包含全部列表視圖的超連接響應數據。
爲了方便接下來的學習,咱們建立一個新的子應用 opt
python3 manage.py startapp opt
配置路由並註冊(此步驟就很少作詳細介紹,須要請移步:)
由於接下來的功能中須要使用到登陸功能,因此咱們使用django內置admin站點並建立一個管理員.
建立管理員之後,訪問admin站點,先修改站點的語言配置,在settings裏修改
建立管理員命令:
python manage.py createsuperuser
運行項目:http://127.0.0.1:8000/admin/
而後就進入到這樣的頁面:
能夠在配置文件settings.py中配置全局默認的認證方案
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', # session認證 'rest_framework.authentication.BasicAuthentication', # 基本認證 ) }
也能夠在每一個視圖中經過設置局部authentication_classess屬性來設置
opt下的urls.py:
urlpatterns = [ path('auth1/', views.Demo1APIView.as_view()), path('auth2/', views.Demo2APIView.as_view()), ]
opt下的views.py:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated, IsAdminUser """用戶的認證和權限識別""" class Demo1APIView(APIView): """只容許登陸後的用戶訪問""" permission_classes = [IsAuthenticated] def get(self, request): """我的中心""" return Response("我的中心") class Demo2APIView(APIView): """只容許管理員訪問""" permission_classes = [IsAdminUser] def get(self, request): """我的中心2""" return Response("我的中心2")
在登陸用戶瀏覽器訪問:
在未登陸的瀏覽器訪問:
auth2的驗證過程是,先用admin頁面裏面新增一個aaqqq用戶,將其用戶設置爲超級用戶,而後讓aaqqq在另一個瀏覽器進行登陸操做,登陸後,將aaqqq用戶設置爲普通用戶,這時這個用戶就是普通用戶且登陸的狀態,全部顯示:
登陸後修改的權限狀態
而超級用戶訪問auth2是:
權限控制能夠限制用戶對於視圖的訪問和對於具體數據對象的訪問。
在執行視圖的dispatch()方法前,會先進行視圖訪問權限的判斷
在經過get_object()獲取具體對象時,會進行模型對象訪問權限的判斷
內置提供的權限:
AllowAny 容許全部用戶
IsAuthenticated 僅經過認證的用戶
IsAdminUser 僅管理員用戶
IsAuthenticatedOrReadOnly 已經登錄認證的用戶能夠對數據進行增刪改操做,沒有登錄認證的只能查看數據。
能夠在配置文件中全局設置默認的權限管理類,如:
REST_FRAMEWORK = { .... 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) }
若是未指明,則採用以下默認配置
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
)
也能夠在具體的視圖中經過局部permission_classes屬性來(在每一個類中)設置。
opt下的urls.py新加內容爲:
# 自定義權限 path('auth3/', views.Demo3APIView.as_view()),
opt下的views.py:
# 如下內容須要新建一個用戶爲xiaoming的用戶,在admin管理裏面進行新建便可。 # 自定義權限 from rest_framework.permissions import BasePermission class MyPermission(BasePermission): def has_permission(self, request, view): """ 針對訪問視圖進行權限判斷 :param request: 本次操做的http請求對象 :param view: 本次訪問路由對應的視圖對象 :return: """ if request.user.username == "xiaoming": return True return False class Demo3APIView(APIView): permission_classes = [MyPermission] def get(self, request): """我的中心3""" return Response("我的中心3")
只有xiaoming才能訪問到auth3
設置了權限就連root都訪問不了:
能夠對接口訪問的頻次進行限制,以減輕服務器壓力。
通常用於付費購買次數,投票等場景使用.
能夠在配置文件中,使用DEFAULT_THROTTLE_CLASSES 和 DEFAULT_THROTTLE_RATES進行全局配置。
REST_FRAMEWORK = { # 限流 'DEFAULT_THROTTLE_CLASSES': ( # 對全局進行設置 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'anon': '3/hour', 'user': '3/minute', } }
DEFAULT_THROTTLE_RATES 可使用 second, minute, hour 或day來指明週期。
也能夠在具體視圖中經過throttle_classess屬性來配置
opt下的urls.py新加內容爲:
# 限流 path('auth4/', views.Demo4APIView.as_view()),
opt下的views.py:
# 限流 from rest_framework.throttling import UserRateThrottle, AnonRateThrottle class Demo4APIView(APIView): throttle_classes = [UserRateThrottle, AnonRateThrottle] # 全局配置後,這裏就不用指定 def get(self, request): """投票頁面""" return Response("投票頁面")
setting.py文件下新加內容:
REST_FRAMEWORK = { # 限流 # 'DEFAULT_THROTTLE_CLASSES': ( # 對全局進行設置 # 'rest_framework.throttling.AnonRateThrottle', # 'rest_framework.throttling.UserRateThrottle' # ), 'DEFAULT_THROTTLE_RATES': { 'anon': '3/hour', 'user': '3/minute', } }
運行測試:
未登陸用戶投票三次後(訪問三次後)只能過一小時才能繼續投票:
登陸的用戶:只須要等待1分鐘
這些都是經過setting裏面設置的。
對於列表數據可能須要根據字段進行過濾,咱們能夠經過添加django-fitlter擴展來加強支持。過濾須要採用以前的序列化器內容:
pip3 install django-filter
在配置文件裏進行註冊:
INSTALLED_APPS = [ ... 'django_filters', # 須要註冊應用, ] # 這個也是全局配置 REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
在視圖中添加filter_fields屬性,指定能夠過濾的字段。
opt下的urls.py新加內容爲:
# 過濾 path('data5/', views.Demo5APIView.as_view()),
opt下的views.py新增:
在沒有過濾的狀況下:
# 過濾 from rest_framework.generics import GenericAPIView, ListAPIView from students.models import Student from .serializers import StudentModelSerializer from django_filters.rest_framework import DjangoFilterBackend class Demo5APIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [DjangoFilterBackend] # 全局配置後,這裏就不用指定了。 filter_fields = ['age', "id"] # 聲明過濾字段
在過濾age=27的狀況下:
http://127.0.0.1:8000/opt/data5/?age=27
對於列表數據,REST framework提供了OrderingFilter過濾器來幫助咱們快速指明數據按照指定字段進行排序。
使用方法:
在類視圖中設置filter_backends,使用rest_framework.filters.OrderingFilter過濾器,REST framework會在請求的查詢字符串參數中檢查是否包含了ordering參數,若是包含了ordering參數,則按照ordering參數指明的排序字段對數據集進行排序。
前端能夠傳遞的ordering參數的可選字段值須要在ordering_fields中指明。
opt下的urls.py新加內容爲:
# 排序 path('data6/', views.Demo6APIView.as_view()),
opt下的views.py新增:
# 排序 from rest_framework.filters import OrderingFilter class Demo6APIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [DjangoFilterBackend, OrderingFilter] # 局部配置會覆蓋全局配置 filter_fields = ['id', "age", "sex"] ordering_fields = ['id', "age"]
首先用過濾age=24出來兩條
而後進行排序id倒序:
http://127.0.0.1:8000/opt/data6/?age=24&ordering=-id
REST framework提供了分頁的支持。
咱們能夠在配置文件中設置全局的分頁方式,如:
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 100 # 每頁數目 }
也可經過自定義Pagination類,來爲視圖添加不一樣分頁行爲。在視圖中經過pagination_clas屬性來指明。
opt下的urls.py新加內容爲:
# 分頁 path('data7/', views.Demo7APIView.as_view()),
opt下的views.py新增:
# 分頁 from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination """1. 自定義分頁器,定製分頁的相關配置""" """ # 頁碼分頁 PageNumberPagination 前端訪問形式:GET http://127.0.0.1:8000/opt/data7/?page=4 page=1 limit 0,10 page=2 limit 10,20 # 偏移量分頁 LimitOffsetPagination 前端訪問形式:GET http://127.0.0.1:8000/opt/data7/?start=4&size=3 start=0 limit 0,10 start=10 limit 10,10 start=20 limit 20,10 """ class StandardPageNumberPagination(PageNumberPagination): """分頁相關配置""" page_query_param = "page" # 設置分頁頁碼關鍵字名 page_size = 3 # 設置每頁顯示數據條數 page_size_query_param = "size" # 設置指定每頁大小的關鍵字名 max_page_size = 5 # 設置每頁顯示最大值 class StandardLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 # 默認限制,默認值與PAGE_SIZE設置一致 limit_query_param = "size" # limit參數名 offset_query_param = "start" # offset參數名 max_limit = 5 # 最大limit限制 class Demo7APIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 分頁 # 頁碼分頁類 pagination_class = StandardPageNumberPagination # 偏移量分頁類 # pagination_class = StandardLimitOffsetPagination
測試(只作了頁碼分頁的測試):