REST framework提供了一個APIView
類,它是Django的View
類的子類。
REST framework主要的幾種view以及他們之間的關系:
mixins
到目前為止,我們使用的創建/獲取/更新/刪除操作和我們創建的任何基於模型的API視圖非常相似。這些常見的行為是在REST框架的mixin類中實現的
Mixin 類提供用於提供基本視圖行為的操作。注意mixin類提供動作方法,而不是直接定義處理程序方法,例如 .get()
和 .post()
, 這允許更靈活的行為組成。
Mixin 類可以從 rest_framework.mixins
導入。
mixins | 作用 | 對應HTTP的請求方法 |
mixins.ListModelMixin | 定義list方法,返回一個queryset的列表 |
GET |
mixins.CreateModelMixin | 定義create方法,創建一個實例 | POST |
mixins.RetrieveModelMixin | 定義retrieve方法,返回一個具體的實例 | GET |
mixins.UpdateModelMixin | 定義update方法,對某個實例進行更新 | PUT/PATCH |
mixins.DestroyModelMixin | 定義delete方法,刪除某個實例 | DELETE |
使用詳解
1.APIView
APIView對django本身的View進行封裝
APIView
類和一般的View
類有以下不同:
-
被傳入到處理方法的請求不會是Django的
HttpRequest
類的實例,而是REST framework的Request
類的實例。 -
處理方法可以返回REST framework的
Response
,而不是Django的HttpRequest
。視圖會管理內容協議,給響應設置正確的渲染器。 -
任何
APIException
異常都會被捕獲,並且傳遞給合適的響應。 -
進入的請求將會經過認證,合適的權限和(或)節流檢查會在請求被派發到處理方法之前
authentication_classes:用戶登錄認證方式,session或者token等等。
permission_classes:權限設置,是否需要登錄等。
throttle_classes:限速設置,對用戶進行一定的訪問次數限制等等
使用APIView
類和使用一般的View
類非常相似,通常,進入的請求會被分發到合適處理方法比如.get()
,或者.post
。另外,很多屬性會被設定在控制API策略的各種切面的類上。
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import authentication, permissions class ListUsers(APIView): """ 列出系統中的所有用戶的視圖。 * 需要token認證 * 只有管理員用戶可以訪問這個視圖。 """ authentication_classes = (authentication.TokenAuthentication,) permission_classes = (permissions.IsAdminUser,) def get(self, request, format=None): """ Return a list of all users. """ usernames = [user.username for user in User.objects.all()] return Response(usernames)
2.GenericAPIView
該類對APIView進行更高層次的封裝,擴展了REST框架的 APIView
類,為標准list和detail view 添加了通常需要的行為。
提供的每個具體通用視圖是通過將 GenericAPIView
與一個或多個mixin類組合來構建的。
from rest_framework import mixins from rest_framework import generics class CourseListView(mixins.ListModelMixin, generics.GenericAPIView): """ 課程列表頁 """ queryset = Course.objects.all() serialize_class = CourseSerializer def get(self, request, *args, **kwargs): # list方法是存在於mixins中的,同理,create等等也是 # GenericAPIView沒有這些方法! return self.list(request, *args, **kwargs)
上述中,繼承了mixins中的ListModelMixin,就是對應把HTTP的get方法轉換調用list方法,list方法會返回queryset的json數據
GenericAPIView對APIView再次封裝,實現了強大功能:
- 加入queryset屬性,可以直接設置這個屬性,不必再將實例化的data,再次傳給seriliazer,系統會自動檢測到。除此之外,可以重載get_queryset(),這樣就不必設置’queryset=*’,這樣就變得更加靈活,可以進行完全的自定義。
- 加入serializer_class屬性與實現get_serializer_class()方法。兩者的存在一個即可,通過這個,在返回時,不必去指定某個serializer。
- 設置過濾器模板:filter_backends。
- 設置分頁模板:pagination_class。
- 加入 lookup_field=”pk”,以及實現了get_object方法,這個用得場景不多,但十分重要。它們兩者的關系同1,要么設置屬性,要么重載方法。它們的功能在於獲取某一個實例時,指定傳進來的后綴是什么。
例如·,獲取具體的某個課程,假設傳進來的URL為:http://127.0.0.1:8000/course/1/,系統會默認這個1指的是course的id。那么,現在面臨一個問題,假設我定義了一個用戶收藏的Model,我想要知道我id為1的主機是否收藏了,我傳進來的URL為:http://127.0.0.1:8000/userfav/1/,系統會默認獲取userfav的id=1的實例,這個邏輯明顯是錯的,我們需要獲取course的id=1的收藏記錄,所以我們就需要用到這個屬性或者重載lookup_field=”course_id”這個方法。
GenericAPIView的不足之處:
既然GenericAPIView以及它相關的View已經完成了許許多多的功能,那么還要ViewSet干嘛!
首先,我們思考一個問題,同樣上面的例子,我們在功能上,要獲取課程的列表,也要獲取某個課程的具體信息。那么怎么實現,按照GenericAPIView,我們可以這樣實現:
class CourseView(ListAPIView,RetrieveAPIView): # 只需要在上面的基礎上,再繼承RetrieveAPIView就ok了。 queryset = Course.objects.all() serialize_class = CourseSerializer
那么,問題來了,我們怎么獲取到是哪個action方法?這個時候,viewset就出場了!
3.Viewset
GenericViewSet繼承了GenericAPIView,依然有get_queryset,get_serialize_class相關屬性與方法,GenericViewSet重寫了as_view方法,可以獲取到HTTP的請求方法。
使用ViewSet
類比使用View
類有兩個主要優點。
- 重復邏輯可以組合成一個類。在上面的示例中,我們只需要指定
queryset
一次,它將在多個視圖中使用。 - 通過使用路由器,我們不再需要處理自己的URL連接。
如:上述問題中我們可以這樣解決
法一:
from rest_framework import viewsets import... class CourseViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): queryset = Course.objects.all() def get_serializer_class(self): # 重寫get_serializer_class方法 if self.action == 'list': return CourseSerializer return CourseDetailSerializer
http請求方法與mixins的方法進行綁定
但GenericViewSet本身依然不存在list, create方法,需要我們與mixins一起混合使用,那么新問題來了?我們依然需要自己寫get、post方法,然后再return list或者create等方法嗎?當然不!重寫as_view的方法為我們提供了綁定的功能,我們在設置url的時候:# 進行綁定 courses = CourseViewSet.as_view({ 'get': 'list', 'post': 'create' }) urlpatterns = [ ... # 常規加入url匹配項 url(r'courses/', CourseViewSet.as_view(), name='courses')]
這樣,我們就將http請求方法與mixins方法進行了關聯。那么還有更簡潔的方法嗎?很明顯,當然有,這個時候,route就登場了!
法二:route方法注冊與綁定
因為我們使用ViewSet類而不是View類,實際上不用自己設計URL conf及綁定HTTP方法。連接resources到views和urls的約定可以使用Router類自動處理。我們需要做的僅僅是正確的注冊View到Router中,然后讓它執行其余操作。新的urls.py代碼如下:
from rest_framework.routers import DefaultRouter router = DefaultRouter() # 只需要實現一次 router.register(r'courses', CourseViewSet, base_name='courses') urlpatterns = [ ... # 只需要加入一次 url(r'^', include(router.urls)),]
route中使用的一定要是ViewSet,用router.register的方法注冊url不僅可以很好的管理url,不會導致url過多而混亂,而且還能實現http方法與mixins中的相關方法進行連接。
ModelViewSet:
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet): # 滿足只有GET方法請求的情景 pass class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): # 滿足所有請求都有的情景 pass
在開發的時候,直接繼承 viewsets.ModelViewSet 就可以了,這樣就不用寫list、create、update等方法了,當然具體問題具體分析,如果你的需求與DRF提供的不一致,那么你就可以重寫相應的方法即可
假如有這樣一個需求,你可能需要過濾查詢集,以確保只返回與當前通過身份驗證的用戶發出的請求相關的結果。
class CourserViewSet(ModelViewSet): """ 每個用戶只可以查看owner屬於自己的條目,可以創建條目 """ def list(self, request, *args, **kwargs): self.queryset = Course.objects.filter(owner=request.user.id) self.serializer_class = CourseSerializer return super(CourserViewSet, self).list(request, *args, **kwargs) def create(self, request, format=None): serializer = CourseSerializer(data=request.data) if serializer.is_valid(): # .save()是調用CourseSerializer中的create()方法 serializer.save(owner=self.request.user) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
這里定義了兩個方法,list方式中我們我們重寫了queryset(就是對返回結果做了一次過濾),然后對於serializer_class指定了一個序列化類。並且我們使用super方法,繼續載入viewset中的list方法,這里只會覆蓋原有list的queryset和serializer_class。而對於create方法,我們則是完全重寫了原viewset中的create方法。這里只是演示一下再ViewSet模式下如何來做工作。原則就是能用ViewSet內置的就是用內置的,內置的不滿足需求就可以重寫部分或全部重寫。
另外,如果只是過濾查詢集,最簡單方法是重寫.get_queryset()
方法即可。重寫此方法允許你以多種不同方式自定義視圖返回的查詢集。
class CourseViewSet(ModelViewSet): """ 每個用戶只可以查看owner屬於自己的條目,可以創建條目 """ serializer_class = CourseSerializer def list(self, request, *args, **kwargs): return Course.objects.filter(owner=request.user.id)
自定義ViewSet基類
要創建基礎視圖集類,提供create
,list
和retrieve
操作,繼承GenericViewSet
和混入所需的操作:
class CreateListRetrieveViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """ A viewset that provides `retrieve`, `create`, and `list` actions. To use it, override the class and set the `.queryset` and `.serializer_class` attributes. """ pass
ps:在開發時是使用ViewSet與mixins方法結合進行可以為我們節省很多功夫
參考:
https://q1mi.github.io/Django-REST-framework-documentation/api-guide/generic-views_zh/
https://www.django-rest-framework.org/api-guide/viewsets/
http://www.ywnds.com/?p=14104