除了上一節講到的方法,使用DRF建立REST風格的資料介面也可以透過CBV(基於類的檢視)的方式。使用CBV建立資料介面的特點是程式碼簡單,開發效率高,但是沒有FBV(基於函式的檢視)靈活,因為使用FBV的方式,資料介面對應的檢視函式執行什麼樣的程式碼以及返回什麼的資料是高度可定製的。下面我們以定製學科的資料介面為例,講解透過CBV方式定製資料介面的具體做法。
修改之前專案中的polls/views.py
,去掉show_subjects
檢視函式,新增一個名為SubjectView
的類,該類繼承自ListAPIView
,ListAPIView
能接收GET請求,它封裝了獲取資料列表並返回JSON資料的get
方法。ListAPIView
是APIView
的子類,APIView
還有很多的子類,例如CreateAPIView
可以支援POST請求,UpdateAPIView
可以支援PUT和PATCH請求,DestoryAPIView
可以支援DELETE請求。SubjectView
的程式碼如下所示。
from rest_framework.generics import ListAPIView
class SubjectView(ListAPIView):
# 透過queryset指定如何獲取學科資料
queryset = Subject.objects.all()
# 透過serializer_class指定如何序列化學科資料
serializer_class = SubjectSerializer
剛才說過,由於SubjectView
的父類ListAPIView
已經實現了get
方法來處理獲取學科列表的GET請求,所以我們只需要宣告如何獲取學科資料以及如何序列化學科資料,前者用queryset
屬性指定,後者用serializer_class
屬性指定。要使用上面的SubjectView
,需要修改urls.py
檔案,如下所示。
urlpatterns = [
path('api/subjects/', SubjectView.as_view()),
]
很顯然,上面的做法較之之前講到的FBV要簡單很多。
如果學科對應的資料介面需要支援GET、POST、PUT、PATCH、DELETE請求來支援對學科資源的獲取、新增、更新、刪除操作,更為簡單的做法是繼承ModelViewSet
來編寫學科檢視類。再次修改polls/views.py
檔案,去掉SubjectView
類,新增一個名為SubjectViewSet
的類,程式碼如下所示。
from rest_framework.viewsets import ModelViewSet
class SubjectViewSet(ModelViewSet):
queryset = Subject.objects.all()
serializer_class = SubjectSerializer
透過檢視ModelViewSet
類的原始碼可以發現,該類共有6個父類,其中前5個父類分別實現對POST(新增學科)、GET(獲取指定學科)、PUT/PATCH(更新學科)、DELETE(刪除學科)和GET(獲取學科列表)操作的支援,對應的方法分別是create
、retrieve
、update
、destroy
和list
。由於ModelViewSet
的父類中已經實現了這些方法,所以我們幾乎沒有編寫任何程式碼就完成了學科資料全套介面的開發,我們要做的僅僅是指出如何獲取到資料(透過queryset
屬性指定)以及如何序列化資料(透過serializer_class
屬性指定),這一點跟上面繼承APIView
的子類做法是一致的。
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
要使用上面的SubjectViewSet
,需要在urls.py
檔案中進行URL對映。由於ModelViewSet
相當於是多個檢視函式的彙總,所以不同於之前對映URL的方式,我們需要先建立一個路由器並透過它註冊SubjectViewSet
,然後將註冊成功後生成的URL一併新增到urlspattern
列表中,程式碼如下所示。
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('api/subjects', SubjectViewSet)
urlpatterns += router.urls
除了ModelViewSet
類外,DRF還提供了一個名為ReadOnlyModelViewSet
的類,從名字上就可以看出,該類是隻讀檢視的集合,也就意味著,繼承該類定製的資料介面只能支援GET請求,也就是獲取單個資源和資源列表的請求。
在使用GET請求獲取資源列表時,我們通常不會一次性的載入所有的資料,除非資料量真的很小。大多數獲取資源列表的操作都支援資料分頁展示,也就說我們可以透過指定頁碼(或類似於頁碼的標識)和頁面大小(一次載入多少條資料)來獲取不同的資料。我們可以透過對QuerySet
物件的切片操作來實現分頁,也可以利用Django框架封裝的Paginator
和Page
物件來實現分頁。使用DRF時,可以在Django配置檔案中修改REST_FRAMEWORK
並配置預設的分頁類和頁面大小來實現分頁,如下所示。
REST_FRAMEWORK = {
'PAGE_SIZE': 10,
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination'
}
除了上面配置的PageNumberPagination
分頁器之外,DRF還提供了LimitOffsetPagination
和CursorPagination
分頁器,值得一提的是CursorPagination
,它可以避免使用頁碼分頁時暴露網站的資料體量,有興趣的讀者可以自行了解。如果不希望使用配置檔案中預設的分頁設定,可以在檢視類中新增一個pagination_class
屬性來重新指定分頁器,通常可以將該屬性指定為自定義的分頁器,如下所示。
from rest_framework.pagination import PageNumberPagination
class CustomizedPagination(PageNumberPagination):
# 預設頁面大小
page_size = 5
# 頁面大小對應的查詢引數
page_size_query_param = 'size'
# 頁面大小的最大值
max_page_size = 50
class SubjectView(ListAPIView):
# 指定如何獲取資料
queryset = Subject.objects.all()
# 指定如何序列化資料
serializer_class = SubjectSerializer
# 指定如何分頁
pagination_class = CustomizedPagination
如果不希望資料分頁,可以將pagination_class
屬性設定為None
來取消預設的分頁器。
如果希望使用CBV定製獲取老師資訊的資料介面,也可以透過繼承ListAPIView
來實現。但是因為要透過指定的學科來獲取對應的老師資訊,因此需要對老師資料進行篩選而不是直接獲取所有老師的資料。如果想從請求中獲取學科編號並透過學科編號對老師進行篩選,可以透過重寫get_queryset
方法來做到,程式碼如下所示。
class TeacherView(ListAPIView):
serializer_class = TeacherSerializer
def get_queryset(self):
queryset = Teacher.objects.defer('subject')
try:
sno = self.request.GET.get('sno', '')
queryset = queryset.filter(subject__no=sno)
return queryset
except ValueError:
raise Http404('No teachers found.')
除了上述方式之外,還可以使用三方庫django-filter
來配合DRF實現對資料的篩選,使用django-filter
後,可以透過為檢視類配置filter-backends
屬性並指定使用DjangoFilterBackend
來支援資料篩選。在完成上述配置後,可以使用filter_fields
屬性或filterset_class
屬性來指定如何篩選資料,有興趣的讀者可以自行研究。