64. drf之5个视图扩展类_9个视图子类_视图集

1. 五个视图扩展类

1.1 五个视图扩展类对应五个方法

查询所有数据
新增一条数据
查询单条数据
修改一条数据
删除一条数据

1.2 写五个类的好处

解耦合,提高代码的可扩展性

这五个类不叫视图类,叫视图扩展类,需要配合GenericAPIView一起用

每个类有一个方法,以后想写哪个接口,就继承哪个类即可

1.3 五个视图扩展类

必须继承 GenericAPIView(提供queryset、serializer_class、get_queryset、get_object等底层能力)

Mixin 只提供实例方法,不会自动映射 GET/POST,需要你手动在def get/post/put/delete里面 return 调用 mixin 方法。

5 个视图扩展类不是视图类,不能单独使用,必需配合GenericAPIView,不能配合APIView

Mixin 类内置方法作用
ListModelMixin list(request,*args,**kwargs) 查询列表,自带过滤、分页逻辑
CreateModelMixin create(request,*args,**kwargs) 创建,序列化校验、save,返回 201
RetrieveModelMixin retrieve(request,*args,**kwargs) 获取单条详情,内部调用get_object()
UpdateModelMixin update(request,*args,**kwargs) PUT 全量更新 / PATCH 局部更新
DestroyModelMixin destroy(request,*args,**kwargs) 删除数据,返回 204 无内容

ListModelMixin源码:查询所有数据(list方法)

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

CreateModelMixin源码:新增一条数据(create方法)

class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

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

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

RetrieveModelMixin源码:查询单条数据(retrieve方法)

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

DestroyModelMixin源码:删除一条数据(destroy方法)

class DestroyModelMixin:
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

UpdateModelMixin源码:修改一条数据(update方法)

class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

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

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

1.4 代码

[1] GenericAPIView + ModelSerializer+ Response写接口

总路由

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/app01/', include('app01.urls')),  # api/v1/的含义是符合restful规范
]

app01的models.py

from django.db import models

# Create your models here.

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

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    authors = models.ManyToManyField(to='Author')

    # 返回出版社详情的函数,变为数据属性
    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr, 'city': self.publish.city}

    # 返回作者列表的函数,变为数据属性
    @property
    def author_l(self):
        l = []
        for i in self.authors.all():  # 获取所有的作者
            l.append({'name': i.name, 'age': i.age, 'addr': i.addr})
        return l

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    school = models.CharField(max_length=32)

    @property
    def add_pre(self):
        return "high_" + self.school

序列化组件serializer.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_l']
        extra_kwargs = {
            'publish': {'write_only': True},
            'authors': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_l': {'read_only': True},
        }

app01的views.py

from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import GenericAPIView

class BookView(GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    # 获取所有
    def get(self, request):
        obj_list = self.get_queryset()
        serializer = self.get_serializer(instance=obj_list, many=True)
        return Response(serializer.data)

    # 新增一条
    def post(self, request):
        # 1 前端携带的数据应该是什么样的? {name:三国演义,price:88,publish_id:1,author_list:[1,2]}
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response(serializer.errors)

class BookDetailView(GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    # 修改一条
    def put(self, request, *args, **kwargs):
        obj = self.get_object()  # 自动根据pk的值去queryset筛选出来
        serializer = self.get_serializer(instance=obj, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response(serializer.errors)

    # 获取一条
    def get(self, request, *args, **kwargs):
        obj = self.get_object()
        serializer = self.get_serializer(instance=obj)
        return Response(serializer.data)

    # 删除一条
    def delete(self, request, *args, **kwargs):
        self.get_object().delete()
        return Response('')

app01的urls.py

from django.urls import path
from .views import BookView, BookDetailView

urlpatterns = [
    path('books/<int:pk>/', BookDetailView.as_view()),
    path('book/', BookView.as_view()),
]

查询所有

image

查询一条

image

[2] GenericAPIView + 5个视图扩展类 + ModelSerializer+ Response写接口

接口函数代码相较于之前的几行精简为一行return

app01的views.py

from rest_framework.generics import GenericAPIView
from .serializer import BookSerializer
from .models import Book
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, ListModelMixin, \
    UpdateModelMixin

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    # 获取所有
    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 BookDetailView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    # 修改一条
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    # 获取一条
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    # 删除一条
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

获取一条

image

获取所有

image

[3] 总结

5个视图扩展类,不是视图类,必须配合GenericAPIView及其子类使用,不能配合APIView使用,因为APIView中没有 get_serializer、get_object、get_queryset

5个视图扩展类,每一个类中只有一个函数,完成5个接口(查询所有、新增一条、修改一条、查询一条、删除一条)中的其中一个,想写多少种接口,就要继承多少种视图扩展类

2. 九个视图子类

2.1 推导

前提:只要 mixin 和 GenericAPI 写在一起,永远 mixin 放左边,GenericAPIView 放最右边。

在很多地方都需要写查询一条、删除一条的接口,已有方法需要在不同的类中重复写[retrieve类_get函数]、[destroy类_delete函数],因此将[retrieve类_get函数]、[destroy类_delete函数]封装到类中

from rest_framework.generics import GenericAPIView
from .serializer import BookSerializer
from .models import Book
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, ListModelMixin, \
    UpdateModelMixin

class SelectOne(RetrieveModelMixin):
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

class DeleteOne(DestroyModelMixin):
    def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)

class BookDetailView(SelectOne, DeleteOne, GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

以上是推导的步骤,而drf已经将[retrieve类_get函数]、[destroy类_delete函数]这些接口封装到了类中,无需重复写类与函数,只需继承类即可,因此用到了9个视图子类

2.2 九个视图子类的应用

[1] 概念

使用:from rest_framework.generics import xxx
本质:5 个 Mixin 扩展类 + GenericAPIView 预先组合完成,已经写好了对应的get/post/put/patch/delete,直接配置queryset、serializer_class即可使用,不用手写 http 方法。

多个扩展类组合的作用是为了避免多次继承 GenericAPIView 
注意:全部都继承 GenericAPIView,自带get_queryset、get_object、get_serializer等能力。

9个视图子类是视图类,不需要配合GenericAPIView或APIView使用

[2] 源码

class CreateAPIView(mixins.CreateModelMixin,
                    GenericAPIView):
    """
    Concrete view for creating a model instance.
    """
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin, GenericAPIView): """ Concrete view for updating a model instance. """ def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs)
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView): """ Concrete view for retrieving, updating or deleting a model instance. """ def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)

[3] 代码应用

(1) 单独写查询所有与新增一条的接口

查询所有

app01的views.py中

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView, CreateAPIView, UpdateAPIView, RetrieveAPIView, DestroyAPIView
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView

# 查询所有
class BookView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

新增一条

class BookView(CreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

(2) 同时写查询所有与新增一条的接口

当需要查询所有与新增一条时,不能同时继承ListAPIView和CreateAPIView

错误写法:这样会继承两次GenericAPIView

class BookView(ListAPIView, CreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

正确写法:使用组合

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

(3) 删除一条的接口

class BookDetailView(DestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

(4) 查询一条、修改一条、删除一条的接口

class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

3. 视图集

3.1 概念

前面学的:GenericAPIView + 5 个 mixin + 9 个视图子类,一个视图类对应一套 url,一个视图只能处理一类请求。
视图集 (ViewSet):把多个 CRUD 逻辑放到同一个类里面,一个类可以处理:查全部、查单个、新增、修改、删除。不再直接写 get/post/put/delete,改成自定义动作方法。

3.2 使用对比

普通视图(GenericAPIView)

# 方法名固定绑定http方法:def get → GET请求,def post → POST请求
class BookView(GenericAPIView):
    def get(self,request):...
    def post(self,request):...

视图集 ViewSet

# 方法名不再是http动词!!!
class BookViewSet(ModelViewSet):
    def list(self,request):   # 获取列表,对应GET
        ...
    def retrieve(self,request): # 获取单个,对应GET
        ...
    def create(self,request): # 创建,对应POST
        ...
    def update(self,request): # 修改,PUT
        ...
    def destroy(self,request): # 删除,DELETE
        ...

list/retrieve/create/update/destroy 叫动作 action,不是原生 http 方法。
视图集本身不会自动绑定 http 请求方法,需要路由来绑定。

3.3 ModelViewSet代码

app01的views.py

使用视图集ModelViewSet,只需要在视图类中写两行即可实现增、删、改、查单个、查全部 5 个接口

from .serializer import BookSerializer
from .models import Book
from rest_framework.viewsets import ModelViewSet

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

app01的urls.py

路由中根据请求的方式设置对应的函数

路由中只需要一个视图类即可实现增、删、改、查单个、查全部 5 个接口的功能,对比之前不需要多个视图类

from django.urls import path
from .views import BookView

urlpatterns = [
    # 获取一条、修改一条、删除一条
    # 使用同一个视图类即可,不需使用多个视图类
    path('book/<int:pk>/', BookView.as_view({'delete': 'destroy', 'get': 'retrieve', 'put': 'update'})),
    # 获取所有、新增一条
    path('book/', BookView.as_view({'get': 'list', 'post': 'create'})),
]

获取一条

image

获取所有

image

3.4 源码

ModelViewSet源码
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

ListModelMixin源码

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

1. 获取所有  前端访问:http://127.0.0.1:8000/api/v1/app01/book/   发起get请求
2. 路由匹配成功:path('book/', BookView.as_view({'get':'list','post':'create'})),
3. 运行BookView 的 list 函数---本质是视图集ModelViewSet的mixins.ListModelMixin的list函数---通过这个list函数返回所有数据

3.5 应用

只需要查询所有和新增一条

通过路由控制

from django.urls import path
from .views import BookView

urlpatterns = [
    # 删除一条
    path('book/<int:pk>/', BookView.as_view({'delete': 'destroy'})),
    # 查询所有
    path('book/', BookView.as_view({'get': 'list'})),
]

3.6 视图集所有类型

[1] 导入

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

[2] ModelViewSet

继承GenericViewSet 和CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin、ListModelMixin,提供了常用的 CRUD 操作(创建、读取、更新和删除)。
5个接口

[3] ReadOnlyModelViewSet

继承GenericViewSet 和 RetrieveModelMixin、ListModelMixin,只提供读取操作。

2个接口
list和retrieve (查询所有和查询一条)

[4] ViewSetMixin

是一个"魔法"混入类,不能单独使用,必须与视图类一起使用,用于改变路由的写法。

之前视图类中的函数只能是请求类型(get、post...),继承ViewSetMixin之后视图类中的函数可以自定义

from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin

class TestView(ViewSetMixin, APIView):
    def login(self,request):
        ...

路由中,请求类型不再对应系统5个接口函数,而是自定义函数

from django.urls import path
from .views import TestView

urlpatterns = [
    path('test01/', TestView.as_view({'post': 'login'})),
]

[5] ViewSet

源码:class ViewSet(ViewSetMixin, views.APIView)

ViewSet = ViewSetMixin + views.APIView

使用ViewSetMixin时有一种用法是同时继承ViewSetMixin和APIView,ViewSet是这两个类的组合

作用:

  用于继承 APIView 并改变路由的写法
  在视图类中的函数可以自定义

[6] GenericViewSet

源码:class GenericViewSet(ViewSetMixin, generics.GenericAPIView)

GenericViewSet = ViewSetMixin + generics.GenericAPIView

使用ViewSetMixin时有一种用法是同时继承ViewSetMixin和GenericAPIView,GenericViewSet是这两个类的组合

作用:

  用于继承 GenericAPIView 并改变路由的写法
  在视图类中的函数可以自定义

4. 接口代码应用

应用一:获取所有图书,删除一条

class BookView(ModelViewSet):
    queryset
    serializer_class

path('book/<int:pk>/', BookView.as_view({'delete': 'destroy'})),
path('book/', BookView.as_view({'get': 'list'})),

应用二:新增接口

class BookView(CreateAPIView):
    queryset
    serializer_class

应用三:登录和注册

ViewSetMixin + GenericAPIView实现自定义函数名而不是使用请求方式函数,并且能够操作数据库

CreateModelMixin实现新增

class UserView(ViewSetMixin,GenericAPIView,CreateModelMixin):
    queryset
    serizlier_class
    def login(self,request): 
        # 取出用户名密码
        # 去数据库校验
        # 校验通过返回登录成功
    def register(self,request):
        return super().create(request)
  path('user/', BookView.as_view({'post':'register'})),

应用四:发送短信

class SendSmSView(ViewSet):
    def send_sms(self,request):
        -取出传入的手机号
        -调用发送短信的功能
        -返回给前端发送成功
path('mobile/', BookView.as_view({'post':'send_sms'})),

 

posted @ 2026-08-13 16:32  pythondjango  阅读(2)  评论(0)    收藏  举报