Rest Framework 視圖和路由
因為涉及到視圖層面了,而且下面的例子會反復用到request.data,所以我決定帶大家稍微看下源碼,感興趣的可以自己深入了解
無論是View還是APIView最開始都是調用as_view()
大致過了下APIView給我們封裝的數據
總結一下
- 舊的request封裝到新request屬性_request里
- 繼承APIView,重新封裝的request.query_params相當於舊的request.GET
- request.data相當於舊的request.POST和request.FILES,且支持json數據類型
第一版封裝
app/views
class BookView(APIView):
def get(self, request):
query_set = Book.objects.all()
book_ser = BookSerializer(query_set, many=True)
return Response(book_ser.data)
def post(self, request):
query_set = request.data
book_ser = BookSerializer(data=query_set)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.validated_data)
else:
return Response(book_ser.errors)
class BookEditView(APIView):
def get(self, request, pk):
query_set = Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(query_set)
return Response(book_ser.data)
def patch(self, request, pk):
query_set = Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(query_set, data=request.data, partial=True)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.validated_data)
else:
return Response(book_ser.errors)
def delete(self, request, pk):
query_set = Book.objects.filter(pk=pk).first()
if query_set:
query_set.delete()
return Response("")
else:
return Response("刪除的書籍不存在")
使用Mixin封裝方法
class GenericAPIView(APIView):
queryset = None
serializer_class = None
def get_queryset(self):
return self.queryset.all()
def get_serializer(self,*args,**kwargs):
return self.serializer_class(*args,**kwargs)
class ListModelMixin:
def list(self,request,*args,**kwargs):
queryset = self.get_queryset()
book_sel = self.get_serializer(queryset,many=True)
return Response(book_sel.data)
class CreateModelMixin:
def create(self,request,*args,**kwargs):
book_sel = self.serializer_class(data=request.data)
if book_sel.is_valid():
book_sel.save()
return Response(book_sel.data)
else:
return Response(book_sel.errors)
class UpdateModelMixin:
def update(self,request,pk,*args,**kwargs):
book_obj = self.get_queryset().filter(pk=pk).first()
book_sel = self.serializer_class(book_obj,data=request.data,partial=True)
if book_sel.is_valid():
book_sel.save()
return Response(book_sel.data)
else:
return Response(book_sel.errors)
class RetrieveModelMixin:
def retrieve(self,request,pk,*args,**kwargs):
book_obj = self.get_queryset().filter(pk=pk).first()
book_sel = self.serializer_class(book_obj)
return Response(book_sel.data)
class DestroyModelMixin:
def destroy(self, request, pk, *args, **kwargs):
queryset = self.get_queryset()
try:
queryset.get(pk=pk).delete()
return Response("")
except Exception as e:
return Response("信息有誤")
# Create your views here.
class BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = models.Book.objects
serializer_class = BookSerializers
def get(self,request,pk,*args,**kwargs):
return self.retrieve(request,pk,*args,**kwargs)
def patch(self,request,pk,*args,**kwargs):
return self.update(request,pk,*args,**kwargs)
def delete(self,request,pk,*args,**kwargs):
return self.destroy(request,pk,*args,**kwargs)
class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = models.Book.objects
serializer_class = BookSerializers
def get(self,request,*args,**kwargs):
return self.list(request,*args,**kwargs)
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)
# 技術點:因為drf中的GenericAPIView提供了queryset和serializer_class,如果要繼承GenericAPIView,
# 則必須重寫這兩個字段,且GenericAPIView提供get_queryset和get_serializer兩個方法
# Mixin類不用繼承其他API,只是單獨提供方法接口,必須跟其他API類混合繼承
感覺經過這么一封裝,每個類中的方法看起來清爽多了,我們還可以繼續封裝
第二版封裝
# 上面我們寫的繼承類太長了~~我們再改改
class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
pass
class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
class BookEditView(RetrieveUpdateDestroyAPIView):
queryset = models.Book.objects
serializer_class = BookSerializers
def get(self,request,pk,*args,**kwargs):
return self.retrieve(request,pk,*args,**kwargs)
def patch(self,request,pk,*args,**kwargs):
return self.update(request,pk,*args,**kwargs)
def delete(self,request,pk,*args,**kwargs):
return self.destroy(request,pk,*args,**kwargs)
class BookView(ListCreateAPIView):
queryset = models.Book.objects
serializer_class = BookSerializers
def get(self,request,*args,**kwargs):
return self.list(request,*args,**kwargs)
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)
感覺只是把類中的繼承稍微簡化了下,並不是特別的優雅,來康康第三版
第三版封裝
我們知道,一般的View執行as_view()不能傳入參數,接下來要介紹的ViewSetMixin,重寫了as_view(actions),可以傳入我們需要的參數
urlpatterns = [
url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
url(r'^retrieve/(?P<pk>\d+)$', BookEditView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})), //這里要注意的是,使用這種傳參的view,傳入的動態id要命名為pk
]
urls.py
from rest_framework.viewsets import ViewSetMixin
# class BookView(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
# queryset = Book.objects.all()
# serializer_class = BookSerializer
# 如果我們再定義一個類
class ModelViewSet(ViewSetMixin, ListCreateAPIView):
pass
class OwnViewSet(ViewSetMixin,RetrieveUpdateDestroyAPIView)
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookEditView(OwnViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
我們現在的視圖就只要寫兩行就可以了,其實我們寫的所有的視圖~框架都幫我們封裝好了,剛剛上面用的例子都是手動封裝
奉獻一張圖來看下我們的繼承順序~~~
drf的路由
我們上面的路由傳參寫的特別多~~框架也幫我們封裝好了~
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"book", BookView)
urlpatterns = [
]
urlpatterns += router.urls
通過框架的路由可以看出,手寫的代碼幾乎沒有了,這里提出一點建議,如果自己的業務邏輯不是跟增刪改查特別耦合
不建議用drf提供的路由組件,因為這樣會暴露很多的接口,不太安全,總之,一般我們很少用到這個組件,還是盡量自己手寫
總結
類的繼承鏈越高,所擁有的功能也就越少,可定制化的程度就越高,盡管上面我們用底層的類,特別輕松的實現了功能,
但需要自定制時,還是繼承APIView實現自己的業務邏輯,總之一切按照業務邏輯來走