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()),
]
查询所有

查询一条

[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)
获取一条

获取所有

[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'})),
]
获取一条

获取所有

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'})),

浙公网安备 33010602011771号