1.DRF的視圖
1.1APIView與View的區別
咱們django中寫CBV的時候繼承的是View,rest_framework繼承的是APIView,那麼他們兩個有什麼不一樣呢~~~
urlpatterns = [
url(r'^book$', BookView.as_view()),
url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
]
咱們能夠看到,無論是View仍是APIView最開始調用的都是as_view()方法~~那咱們走進源碼看看~~
咱們能看到,APIView繼承了View, 而且執行了View中的as_view()方法,最後把view返回了,用csrf_exempt()方法包裹後去掉了csrf的認證。
那咱們看看View中的as_view()方法作了什麼~
咱們看到了~在View中的as_view方法返回了view函數,而view函數執行了self.dispatch()方法~~可是這裏的dispatch方法應該是咱們APIView中的~~
咱們去initialize_request中看下把什麼賦值給了request,而且賦值給了self.request, 也就是咱們在視圖中用的request.xxx究竟是什麼~~
咱們看到,這個方法返回的是Request這個類的實例對象~~咱們注意咱們看下這個Request類中的第一個參數request,是咱們走咱們django的時候的原來的request~
咱們看到了,這個Request類把原來的request賦值給了self._request, 也就是說之後_request是咱們老的request,新的request是咱們這個Request類~~
那咱們繼承APIView以後請求來的數據都在哪呢~~
咱們用了rest_framework框架之後,咱們的request是從新封裝的Request類~
request.query_params 存放的是咱們get請求的參數
request.data 存放的是咱們全部的數據,包括post請求的以及put,patch請求~~~
相比原來的django的request,咱們如今的request更加精簡,清晰了~~~
如今咱們知道了APIView和View的一些區別~~固然還有~~後面咱們還會說~~
咱們寫的視圖可能對多個表進行增刪改查,就致使咱們的視圖特別多重複的代碼~~
那麼咱們嘗試着來進行封裝一下~~
1.2第一次封裝
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, id):
query_set = Book.objects.filter(id=id).first()
book_ser = BookSerializer(query_set)
return Response(book_ser.data)
def patch(self, request, id):
query_set = Book.objects.filter(id=id).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, id):
query_set = Book.objects.filter(id=id).first()
if query_set:
query_set.delete()
return Response("")
else:
return Response("刪除的書籍不存在")
# 第一次封裝
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(object):
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
class CreateModelMixin(object):
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.validated_data)
else:
return Response(serializer.errors)
class RetrieveModelMixin(object):
def retrieve(self, request, id, *args, **kwargs):
book_obj = self.get_queryset().filter(pk=id).first()
book_ser = self.get_serializer(book_obj)
return Response(book_ser.data)
class UpdateModelMixin(object):
def update(self, request, id, *args, **kwargs):
book_obj = self.get_queryset().filter(pk=id).first()
book_ser = self.get_serializer(book_obj, 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)
class DestroyModelMixin(object):
def destroy(self, request, id, *args, **kwargs):
queryset = self.get_queryset()
try:
queryset.get(pk=id).delete()
return Response("")
except Exception as e:
return Response("信息有誤")
# 咱們把公共的部分抽出來 這樣無論寫多少表的增刪改查都變的很簡單
# 這樣封裝後咱們的視圖會變成這樣
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, id, *args, **kwargs):
return self.retrieve(request, id, *args, **kwargs)
def patch(self, request, id, *args, **kwargs):
return self.update(request, id, *args, **kwargs)
def destroy(self, request, id, *args, **kwargs):
return self.delete(request, id, *args, **kwargs)
咱們封裝的GenericAPIView,包括封裝每一個方法的類,其實框架都幫咱們封裝好了~~
咱們能夠直接繼承這些類~~來實現上面的視圖~~但是還有沒有更簡單的方法呢~咱們再次封裝一下~~
1.3二次封裝
# 上面咱們寫的繼承類太長了~~咱們再改改
class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
pass
class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
class BookView(ListCreateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookEditView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, id, *args, **kwargs):
return self.retrieve(request, id, *args, **kwargs)
def patch(self, request, id, *args, **kwargs):
return self.update(request, id, *args, **kwargs)
def delete(self, request, id, *args, **kwargs):
return self.delete(request, id, *args, **kwargs)
此次咱們只是讓繼承變的簡單了一點而已,好像並無什麼大的進步~~
咱們可不能夠把這兩個視圖合併成一個視圖呢~~~框架給咱們提供了一個路由傳參的方法~~
咱們看下ViewSetMixin
actions這個默認參數其實就是咱們路由能夠進行傳參了~~~
下面這個循環~能夠看出~咱們要傳的參數是一個字段~key應該是咱們的請求方式,value應該對應咱們處理的方法~
這樣咱們每一個視圖就不用在寫函數了~由於已經和內部實現的函數相對應了~
1.4三次封裝
#urls.py
urlpatterns = [
# url(r'^book$', BookView.as_view()),
# url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
url(r'^book/(?P<pk>\d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),
]
from rest_framework.viewsets import ViewSetMixin
# class BookView(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
# queryset = Book.objects.all()
# serializer_class = BookSerializer
# 若是咱們再定義一個類
class ModelViewSet(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
pass
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
咱們如今的視圖就只要寫兩行就能夠了~~~
其實咱們寫的全部的視圖~框架都幫咱們封裝好了~
注意一點~~
用框架封裝的視圖~咱們url上的那個關鍵字參數要用pk~~系統默認的~~
奉獻一張圖來看下咱們的繼承順序~~~
2.DRF的路由
from django.contrib import admin
from django.urls import path,include
from app.views import BookView
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"book", BookView)
urlpatterns = [
path('admin/', admin.site.urls),
# path(r'book/', BookView.as_view({"get": "list", "post": "create"})),
# path('book/<int:pk>/', BookView.as_view({"get": "list", "put": "update", "delete": "destroy"})),
]
urlpatterns += router.urls
咱們能夠看到~~經過框架咱們能夠把路由視圖都變的很是簡單~~
可是須要自定製的時候仍是須要咱們本身用APIView寫~~當不須要那麼多路由的時候~也不要用這種路由註冊~~
3.案例展現
#views.py
from app.models import Book, Publisher
from rest_framework.viewsets import ModelViewSet
from app.serializers import BookSerializer
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
#urls.py
from django.contrib import admin
from django.urls import path,include
from app.views import BookView
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"book", BookView)
urlpatterns = [
path('admin/', admin.site.urls),
# path(r'book/', BookView.as_view({"get": "list", "post": "create"})),
# path('book/<int:pk>/', BookView.as_view({"get": "list", "put": "update", "delete": "destroy"})),
]
urlpatterns += router.urls
#自定製的urls.py
默認的urls配置,暴露了過多的方法,有的時候,咱們不想暴露put或delete,就須要自定製url
from django.contrib import admin
from django.urls import path,include
from app.views import BookView
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
urlpatterns = [
path('admin/', admin.site.urls),
path(r'book/', BookView.as_view({"get": "list", "post": "create"})),
#注意get對應retrive,只獲取一條數據
#不但願有delete,就去掉delete便可。
path('book/<int:pk>/', BookView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]