DRF 檢視元件

HammerZe發表於2022-04-01

DRF 檢視元件

ModelSerializer序列化器實戰

DRF框架提供了很多通用的檢視基類與擴充套件類,上篇使用的APIView是比較偏Base的,檢視的使用更加簡化了程式碼,這裡介紹一下其他檢視的用法

Django REST framwork 提供的檢視的主要作用:

  • 控制序列化器的執行(檢驗、儲存、轉換資料)
  • 控制資料庫查詢的執行

先來看看這其中的人情世故:兩個檢視基本類,五個擴充套件類,九個檢視子類,檢視集方法,檢視集··

img

檢視元件大綱

兩個檢視基本類

匯入

from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
  • APIView:DRF最頂層檢視類
  • GenericAPIView:DRF通用檢視類

五個擴充套件類

擴充套件類不是檢視類,沒有整合APIView,需要配合GenericAPIView使用,因為五個擴充套件類的實現需要呼叫GenericAPIView提供的序列化器與資料庫查詢的方法

主要是用來對資料進行增刪改查

匯入

from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
  • CreateModelMixin
  • ListModelMixin
  • DestroyModelMixin
  • RetrieveModelMixin
  • UpdateModelMixin

九個子類檢視

匯入

from rest_framework.generics import  CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView

檢視子類其實可以理解為GenericAPIView通用檢視類和Mixin擴充套件類的排列組合組成的,底層事通過封裝和繼承來寫

  • CreateAPIView

    提供 post 方法
    繼承自: GenericAPIView、CreateModelMixin
    
  • ListAPIView

    提供 get 方法
    繼承自:GenericAPIView、ListModelMixin
    
  • DestroyAPIView

    提供 delete 方法
    繼承自:GenericAPIView、DestoryModelMixin
    
  • RetrieveAPIView

    提供 get 方法
    繼承自: GenericAPIView、RetrieveModelMixin
    
  • UpdateAPIView

    提供 put 和 patch 方法
    繼承自:GenericAPIView、UpdateModelMixin
    
  • ListCreateAPIView

    提供get 和 post方法
    繼承自:ListModelMixin、CreateModelMixin、GenericAPIView
    
  • RetrieveUpdateAPIView

    提供 get、put、patch方法
    繼承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
    
  • RetrieveDestroyAPIView

    提供:get、delete方法
    繼承自:RetrieveModelMixin、DestroyModelMixin、GenericAPIView
    
  • RetrieveUpdateDestroyAPIView

    提供 get、put、patch、delete方法
    繼承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
    

檢視集

匯入

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin

常用檢視集父類

  • ModelViewSet:繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

  • ReadOnlyModelViewSet:繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。

  • ViewSet:繼承自APIView與ViewSetMixin,作用也與APIView基本類似,提供了身份認證、許可權校驗、流量管理等。ViewSet主要通過繼承ViewSetMixin來實現在呼叫as_view()時傳入字典({'get':'list'})的對映處理工作。

    • 在ViewSet中,沒有提供任何動作action方法,需要我們自己實現action方法。
  • GenericViewSet:使用ViewSet通常並不方便,因為list、retrieve、create、update、destory等方法都需要自己編寫,而這些方法與前面講過的Mixin擴充套件類提供的方法同名,所以我們可以通過繼承Mixin擴充套件類來複用這些方法而無需自己編寫。但是Mixin擴充套件類依賴與GenericAPIView,所以還需要繼承GenericAPIView。

    • GenericViewSet就幫助我們完成了這樣的繼承工作,繼承自GenericAPIView與ViewSetMixin,在實現了呼叫as_view()時傳入字典(如{'get':'list'}`)的對映處理工作的同時,還提供了GenericAPIView提供的基礎方法,可以直接搭配Mixin擴充套件類使用。

魔法類

  • ViewSetMixin:控制自動生成路由

一覽表

image

DRF中檢視的“七十二變”

第一層是繼承APIView寫,第二層基於基於GenericAPIView寫,第三層基於GenericAPIView+五個擴充套件類寫,第四層通過九個檢視子類來寫,第五層是通過ViewSet寫

ps:第幾層是我意淫出來的詞不要介意~,一層更比一層牛,欲練此功必先自宮!!!

第一層:基於APIview的五個介面

class BookView(APIView):
    def get(self, requets):
        # 序列化
        book_list = models.Book.objects.all()
        # 序列化多條資料many=True
        ser = serializer.BookSerializer(instance=book_list, many=True)
        return Response(ser.data)

    def post(self, request):
        # 獲取反序列化資料
        ser = serializer.BookSerializer(data=request.data)
        if ser.is_valid():
            # 校驗通過存入資料庫,不需要重寫create方法了
            ser.save()
            return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
        # 校驗失敗
        return Response({'code': 101, 'msg': '校驗未通過', 'error': ser.errors})


class BookViewDetail(APIView):
    def get(self, request, pk):
        book = models.Book.objects.filter(pk=pk).first()
        ser = serializer.BookSerializer(instance=book)
        return Response(ser.data)

    def put(self, request, pk):
        book = models.Book.objects.filter(pk=pk).first()
        # 修改,instance和data都要傳
        ser = serializer.BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            # 校驗通過修改,不需要重寫update
            ser.save()
            return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
        # 校驗不通過
        return Response({'code:': 102, 'msg': '校驗未通過,修改失敗', 'error': ser.errors})

    def delete(self, request, pk):
        models.Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '刪除成功'})

第一層五個介面demo

ModelSerializer序列化器實戰 - HammerZe - 部落格園 (cnblogs.com)

第二層:基於GenericAPIView的五個介面

常用類屬性:

-GenericAPIView   繼承了APIView,封裝了一些屬性和方法,跟資料庫打交道
  	-queryset = None # 指定序列化集
    -serializer_class = None  # 指定序列化類
    -lookup_field = 'pk'  # 查詢單條,分組分出來的引數,轉換器物件引數的名字
    -filter_backends   # 過濾排序功能會用它
    -pagination_class  # 分頁功能
    
    -get_queryset()  # 獲取要序列化的資料,後期可能會重寫
    -get_object()    # 通過lookup_field查詢的
    -get_serializer()  # 使用它序列化
    -get_serializer_class() # 返回序列化類 ,後期可能重寫
    
    
    
demo:
# 指定序列化集
queryset = models.Book.objects.all()
# 指定序列化類
serializer_class = serializer.BookSerializer

第二層五個介面demo

from rest_framework.response import Response


from app01 import models
from app01 import serializer
from rest_framework.generics import GenericAPIView
# 書檢視類
class BookView(GenericAPIView):
    # 指定序列化集
    queryset = models.Book.objects.all()
    # 指定序列化類
    serializer_class = serializer.BookSerializer
    def get(self, requets):
        # obj = self.queryset()
        obj = self.get_queryset() # 等同於上面
        # ser = self.get_serializer_class()(instance=obj,many=True)
        ser = self.get_serializer(instance=obj,many=True) # 等同於上面
        return Response(ser.data)

    def post(self, request):
        # 獲取反序列化資料
        # ser = serializer.BookSerializer(data=request.data)
        ser = self.get_serializer(data = request.data)
        if ser.is_valid():
            # 校驗通過存入資料庫,不需要重寫create方法了
            ser.save()
            return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
        # 校驗失敗
        return Response({'code': 101, 'msg': '校驗未通過', 'error': ser.errors})


class BookViewDetail(GenericAPIView):
    # 指定序列化集
    queryset = models.Book.objects.all()
    # 指定序列化類
    serializer_class = serializer.BookSerializer
    def get(self, request, pk):
        # book = models.Book.objects.filter(pk=pk).first()
        book = self.get_object() # 根據pk拿到單個物件
        # ser = serializer.BookSerializer(instance=book)
        ser = self.get_serializer(instance=book)
        return Response(ser.data)

    def put(self, request, pk):
        # book = models.Book.objects.filter(pk=pk).first()
        book = self.get_object()
        # 修改,instance和data都要傳
        # ser = serializer.BookSerializer(instance=book, data=request.data)
        ser = self.get_serializer(instance=book,data=request.data)
        if ser.is_valid():
            # 校驗通過修改,不需要重寫update
            ser.save()
            return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
        # 校驗不通過
        return Response({'code:': 102, 'msg': '校驗未通過,修改失敗', 'error': ser.errors})

    def delete(self, request, pk):
        # models.Book.objects.filter(pk=pk).delete()
        self.get_object().delete()
        return Response({'code': 100, 'msg': '刪除成功'})

路由

path('books/', views.BookView.as_view()),
path('books/<int:pk>', views.BookViewDetail.as_view())

總結:到第二層只需修改querysetserializer_class類屬性即可,其餘都不需要修改

注意:雖然pk沒有在orm語句中過濾使用,但是路由分組要用,所以不能刪,或者寫成*args **kwargs接收多餘的引數,且路由轉換器必須寫成pk

# 原始碼
lookup_field = 'pk'
lookup_url_kwarg = None

get_queryset()方法可以重寫,如果我們需要在一個檢視類內操作另外表模型,來指定序列化的資料

class BookViewDetail(GenericAPIView):
    queryset = models.Book.objects.all()
    ···
    '''
    指定序列化資料的格式:
    self.queryset()
    self.get_queryset() # 等同於上面
    queryset = models.Book.objects.all()
    '''
	# 可以重寫get_queryset方法在book檢視類裡操作作者模型
    def get_queryset(self,request):
        if self.request.path == '/user'
        return Author.objects.all()
    ···
    # 這樣序列化的資料就不一樣了,根據不同的條件序列化不同的資料
    
    '''當然還可以通過重寫get_serializer_class來返回其他序列化器類'''

第三層:基於GenericAPIView+五個檢視擴充套件類寫

五個檢視擴充套件類:from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin

通過GenericAPIView+檢視擴充套件類來使得程式碼更簡單,一個介面對應一個擴充套件類,注意擴充套件類不是檢視類

  • ListModelMixin:獲取所有API,對應list()方法
  • CreateModelMixin:新增一條API,對應create()方法
  • UpdateModelMixin:修改一條API,對應update()方法
  • RetrieveModelMixin:獲取一條API,對應retrieve()方法
  • DestroyModelMixin:刪除一條API,對應destroy()方法

注意:CreateModelMixin擴充套件類提供了更高階的方法,可以通過重寫來校驗資料存入

    def perform_create(self, serializer):
        serializer.save()

第三層五個介面demo

from app01 import models
from app01 import serializer
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin

# 獲取所有和新增API
class BookView(ListModelMixin,CreateModelMixin,GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
    def get(self, request):
       return super().list(request)

    def post(self, request):
        return super().create(request)

# 獲取刪除修改單個API
class BookViewDetail(UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
    def get(self, request, *args,**kwargs):
        return super().retrieve(request, *args,**kwargs)

    def put(self, request, *args,**kwargs):
        return super().update(request, *args,**kwargs)

    def delete(self, request, *args,**kwargs):
        return super().destroy(request, *args,**kwargs)

總結

通過進一次封裝+繼承程式碼也變得越來越少了

GenericAPIView速寫五個介面demo

模型

from django.db import models


# Create your models here.

# build four model tables

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(decimal_places=2, max_digits=5)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name

    # 自定製欄位
    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.city}


    @property
    def author_list(self):
        l = []
        print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>

        for author in self.authors.all():
            print(author.author_detail) # AuthorDetail object (1)
            l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr})
        return l


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

    @property
    def authordetail_info(self):
        return {'phone':self.author_detail.telephone,'addr':self.author_detail.addr}


class AuthorDetail(models.Model):
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

序列化器

from django.db import models


# Create your models here.

# build four model tables

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(decimal_places=2, max_digits=5)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name

    # 自定製欄位
    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.city}


    @property
    def author_list(self):
        l = []
        print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>

        for author in self.authors.all():
            print(author.author_detail) # AuthorDetail object (1)
            l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr})
        return l


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

    @property
    def authordetail_info(self):
        return {'phone':self.author_detail.telephone,'addr':self.author_detail.addr}


class AuthorDetail(models.Model):
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

檢視

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, \
    UpdateModelMixin

from app01 import models
from app01 import serializer


# 書檢視類
class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

    def get(self, request):
        return super().list(request)

    def post(self, request):
        return super().create(request)


class BookViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

    def get(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


# 作者
class AuthorView(ListModelMixin, CreateModelMixin, GenericAPIView):
    queryset = models.Author.objects.all()
    serializer_class = serializer.AuthorSerializer

    def get(self, request):
        return super().list(request)

    def post(self, request):
        return super().create(request)


class AuthorViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
    queryset = models.Author.objects.all()
    serializer_class = serializer.AuthorSerializer

    def get(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


# 作者詳情

class AuthorDetailView(ListModelMixin, CreateModelMixin, GenericAPIView):
    queryset = models.AuthorDetail.objects.all()
    serializer_class = serializer.AuthorDetailSerializer

    def get(self, request):
        return super().list(request)

    def post(self, request):
        return super().create(request)


class OneAuthorViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
    queryset = models.AuthorDetail.objects.all()
    serializer_class = serializer.AuthorDetailSerializer

    def get(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


# 出版社
class PublishView(ListModelMixin, CreateModelMixin, GenericAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = serializer.PublishSerializer

    def get(self, request):
        return super().list(request)

    def post(self, request):
        return super().create(request)


class PublishViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = serializer.PublishSerializer

    def get(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)

路由

from django.contrib import admin
from django.urls import path

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 書
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>', views.BookViewDetail.as_view()),

    # 作者
    path('authors/', views.AuthorView.as_view()),
    path('authors/<int:pk>', views.AuthorViewDetail.as_view()),

    # 作者詳情
    path('authorsdetail/', views.AuthorDetailView.as_view()),
    path('authorsdetail/<int:pk>', views.OneAuthorViewDetail.as_view()),

    # 出版社
    path('publish/', views.PublishView.as_view()),
    path('publish/<int:pk>', views.PublishViewDetail.as_view()),
]

Postman以及測完,請放心使用~

第四層:GenericAPIView+九個檢視子類寫五個介面

匯入檢視子類from rest_framework.generics import CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView

使用哪個繼承哪個就可以了,具體可以看繼承的父類裡有什麼方法不需要刻意去記

from rest_framework.generics import  CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView

# 1、查詢所有,新增API
class BookView(ListCreateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
# 2、新增介面
class BookView(CreateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
# 3、查詢介面
class BookView(ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

# 4、查詢單個,修改一個,刪除一個介面
class BookViewDetail(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

# 5、查詢單個介面
class BookViewDetail(RetrieveAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

# 6、修改單個介面
class BookViewDetail(UpdateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

# 7、刪除單個介面
class BookViewDetail(DestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

# 8、查詢單個、修改介面
class BookViewDetail(RetrieveUpdateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer


# 9、查詢單個、刪除介面
class BookViewDetail(RetrieveDestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

'''上述共九個檢視子類,九九歸一劍訣~'''

# 更新和刪除介面自己整合
class BookViewDetail(UpdateAPIView,DestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

第四層快速寫五個介面demo

from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView

from app01 import models
from app01 import serializer

class BookView(ListCreateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

class BookViewDetail(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
'''其餘的和第三層一樣'''

第五層:基於ViewSet寫五個介面

檢視集匯入from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin

路由匯入from rest_framework.routers import SimpleRouter,DefaultRouter

基於ViewSet檢視集寫,需要我們配置路由

  • 方法一

    from django.urls import path, include
    from rest_framework.routers import SimpleRouter
    from app01 import views
    
    router = SimpleRouter()
    router.register('books', views.BookView, 'books')
    urlpatterns = [
        ...
    ]
    urlpatterns += router.urls
    '''
    register(self, prefix, viewset, basename=None)
    prefix:路由url字首
    viewset:處理請求的viewset類
    basename:路由名稱的字首,一般和prefix寫成一樣就行
    '''
    # 等同於
    path('books/'),include(router.urls)
    path('books/<int:pk>'),include(router.urls)
    
  • 方法二

    router = SimpleRouter()
    router.register('books', views.BookView, 'books')
    
    urlpatterns = [
        ...
        url(r'^', include(router.urls))
    ]
    
    
    # 生成兩種路由
    path('/api/v1'),include(router.urls)
    # [<URLPattern '^books/$' [name='books-list']>, <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>]
    # 等同於自己配的
    path('/api/v1/books/'),include(router.urls)
    path('/api/v1/books/<int:pk>'),include(router.urls)
    

    異同

    • 同:方法一和方法二都可以自動生成路由,代替了下面的路由

      path('books/', views.BookView.as_view()),
      path('books/<int:pk>', views.BookViewDetail.as_view()),
      
    • 異:方法二可以拼接路徑,如果不拼接是和方法一一樣的

兩種不同的路由

image

image

第五層基於ModelViewSet檢視集寫五個介面demo

views.py

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
class BookView(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

urls.py

from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter

from app01 import views

router = SimpleRouter()
router.register('books', views.BookView, 'books')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/',include(router.urls)),
]

ps:剩下的都一樣~

ReadOnlyModelViewSet檢視集

繼承該ReadOnlyModelViewSet檢視集的作用是隻讀,只做查詢,修改刪除等操作不允許

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
class BookView(ReadOnlyModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer

image

兩個檢視集總結

  • ModelViewSet可以寫五個介面,而ReadOnlyModelViewSet只能寫兩個介面

本質

  • ModelViewSet繼承了五個檢視擴充套件類+GenericViewSet,GenericViewSet繼承了ViewSetMixin+GenericAPIView

    PS:ViewSetMixin控制了路由寫法

  • ReadOnlyModelViewSet繼承了RetrieveModelMixin+ListModelMixin+GenericViewSet

其他檢視集

ViewSet

ViewSet = ViewSetMixin+APIView


class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    """
    pass

GenericViewSet

GenericViewSet = ViewSetMixin+GenericAPIView

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

ViewSetMixin

魔術檢視類,控制自動生成路由,可以通過組合繼承,以前的寫法可以繼續使用,但是如果要自動生成路由必須得繼承ViewSetMixin及其子類;或者選擇繼承ViewSet、GenericViewSet

class ViewSetMixin:
    """
    This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the 'GET' and 'POST' methods
    to the 'list' and 'create' actions...

    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """
這就是魔法。  
 
重寫' .as_view() ',以便它接受一個' actions '關鍵字執行  
將HTTP方法繫結到資源上的動作。  
 
例如,建立繫結'GET'和'POST'方法的具體檢視  
到“列表”和“建立”動作…  
 
= MyViewSet檢視。 As_view ({'get': 'list', 'post': 'create'})  

總結

  • 第一層:基於APIView寫檢視,get、post、put、delete都需要自己寫,序列化的資料和序列化類需要獲取後指定

    class BookView(APIView):
        def get(self, requets):
            book_list = models.Book.objects.all()
            ser = serializer.BookSerializer(instance=book_list, many=True)
            return Response(ser.data)
    
  • 第二層:基於GenericAPIView寫檢視,優化了檢視類內序列化資料和序列化類的程式碼冗餘問題,通過querysetserializer_class指定序列化集和序列化器即可,一個檢視類內寫一次即可,最後通過get_querysetget_serializer方法處理

    class BookView(GenericAPIView):
        queryset = models.Book.objects.all()
        serializer_class = serializer.BookSerializer
        def get(self, requets):
            obj = self.get_queryset() 
            ser = self.get_serializer(instance=obj,many=True) 
            return Response(ser.data)
    
  • 第三層:基於GenericAPIView+5個檢視擴充套件類寫檢視,每個擴充套件類對應一個介面,更加細化,通過繼承父類(擴充套件類)減少了程式碼的冗餘

    class BookView(ListModelMixin,CreateModelMixin,GenericAPIView):
        queryset = models.Book.objects.all()
        serializer_class = serializer.BookSerializer
        def get(self, request):
           return super().list(request)
    
  • 第四層,基於九個檢視子類寫,檢視子類將擴充套件類和GenericAPIView封裝到一塊,使得我們要寫的程式碼更少了,總之就是牛逼~

    class BookView(ListCreateAPIView):
        queryset = models.Book.objects.all()
        serializer_class = serializer.BookSerializer
    
  • 第五層,基於ViewSet寫檢視,這樣以來5個介面就都在一個檢視類內,程式碼更少了,但是可擴充套件性低了,路由也是問題,get所有和get一條路由衝突需要修改

    class BookView(ModelViewSet):
        queryset = models.Book.objects.all()
        serializer_class = serializer.BookSerializer
    '''路由'''
    router = SimpleRouter()
    router.register('books', views.BookView, 'books')
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/v1/',include(router.urls)),
    ]
    

補充

檢視集中定義附加action動作

在檢視集中,除了上述預設的方法動作外,還可以新增自定義動作。

舉例:

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def login(self,request):
        """學生登入功能"""
        return Response({"message":"登入成功"})

url的定義

urlpatterns = [
    path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students8/(?P<pk>\d+)/",
            views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),

    path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"}))

]

action屬性

在檢視集中,我們可以通過action物件屬性來獲取當前請求檢視集時的action動作是哪個。

例如:

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def get_new_5(self,request):
        """獲取最近新增的5個學生資訊"""
        # 運算元據庫
        print(self.action) # 獲取本次請求的檢視方法名
        
        
通過路由訪問到當前方法中.可以看到本次的action就是請求的方法名


累死?,有錯誤請指正~感謝

相關文章