7

DRF 视图组件 - HammerZe

 2 years ago
source link: https://www.cnblogs.com/48xz/p/16089640.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

DRF 视图组件

ModelSerializer序列化器实战

DRF框架提供了很多通用的视图基类与扩展类,上篇使用的APIView是比较偏Base的,视图的使用更加简化了代码,这里介绍一下其他视图的用法

Django REST framwork 提供的视图的主要作用:

  • 控制序列化器的执行(检验、保存、转换数据)
  • 控制数据库查询的执行

先来看看这其中的人情世故:两个视图基本类,五个扩展类,九个视图子类,视图集方法,视图集··

视图组件大纲

两个视图基本类

导入

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:控制自动生成路由

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()),
      
    • 异:方法二可以拼接路径,如果不拼接是和方法一一样的

两种不同的路由

第五层基于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

两个视图集总结#

  • 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就是请求的方法名


累死🐵,有错误请指正~感谢

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK