Django REST framework 源码剖析-视图类详解(Views)
Django REST framework视图图解
- 视图类(View)
视图是DRF中处理用户请求的基本单元。它们可以是函数视图(FBV)或类视图(CBV)。函数视图使用函数来处理请求,而类视图则使用类来处理请求。类视图默认会自动dispatch特定的HTTP方法,如GET、POST等,不需要像函数视图那样通过单独的条件判断来处理不同的HTTP方法。此外,类视图可以使用面向对象的技术,如Mixin(多继承、混用),将代码拆解成可复用的组件,GenericAPIView可以与ListModelMixin、CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin等Mixin结合使用,以实现增删改查等功能。
- 视图集(ViewSet)
视图集是DRF中用于组织相关视图的集合。它们继承自APIView或GenericAPIView,并且可以定义多个方法(如list、create、retrieve、update、destroy等),这些方法对应不同的HTTP请求。视图集可以解决路由匹配和合并的问题,并且方法名可以自定义,路由匹配规则也需要相应修改。视图集还可以与Mixin结合使用,以实现更复杂的业务逻辑。
视图类
APIView
- rest_framework.views.APIView
- APIView是REST framework提供的所有视图的基类,继承自Django的View父类
APIView与View的不同之处
- 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象
- 视图方法可以返回REST framework的Response对象,视图会为响应数据- 设置(render)符合前端要求的格式
- 任何APIException异常都会被捕获到,并且处理成合适的响应信息
- 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制
支持定义的类属性
- authentication_classes 列表或元祖,身份认证类
- permissoin_classes 列表或元祖,权限检查类
- throttle_classes 列表或元祖,流量控制类
APIViews 示例
from rest_framework.views import APIView
from rest_framework.response import Response
# url(r'^students/$', views.StudentsAPIView.as_view()),
class StudentsAPIView(APIView):
def get(self, request):
data_list = Student.objects.all()
serializer = StudentModelSerializer(instance=data_list, many=True)
return Response(serializer.data)
GenericAPIView
- rest_framework.generics.GenericAPIView
- 继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持,通常在使用时,可搭配一个或多个Mixin扩展类
属性
serializer_class 指明视图使用的序列化器
queryset 指明使用的数据查询集
pagination_class 指明分页控制类
filter_backends 指明过滤控制后端
方法
get_serializer_class(self)
- 当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了
- 返回序列化器类,默认返回serializer_class,可以重写,例如:
def get_serializer_class(self):
if self.request.user.is_staff:
return FullAccountSerializer
return BasicAccountSerializer
get_serializer(self, args, *kwargs)
- 返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法
- 该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用
- request 当前视图的请求对象
- view 当前请求的类视图对象
- format 当前请求期望返回的数据格式
get_queryset(self)
- 返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:
def get_queryset(self):
user = self.request.user
return user.accounts.all()
get_object(self)
- 返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用
在试图中可以调用该方法获取详情信息的模型类对象 - 若详情访问的模型类对象不存在,会返回404
- 该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问, 例如:
# url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
class BookDetailView(GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象
serializer = self.get_serializer(book)
return Response(serializer.data)
GenericAPIView 示例
from rest_framework.generics import GenericAPIView
from students.models import Student
from .serializers import StudentModelSerializer, StudentModel2Serializer
from rest_framework.response import Response
class StudentsGenericAPIView(GenericAPIView):
# 本次视图类中要操作的数据[必填]
queryset = Student.objects.all()
# 本次视图类中要调用的默认序列化器[选填]
serializer_class = StudentModelSerializer
def get(self, request):
"""获取所有学生信息"""
serializer = self.get_serializer(instance=self.get_queryset(), many=True)
return Response(serializer.data)
def post(self,request):
data = request.data
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
serializer = self.get_serializer(instance=instance)
return Response(serializer.data)
class StudentGenericAPIView(GenericAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def get_serializer_class(self):
"""重写获取序列化器类的方法"""
if self.request.method == "GET":
return StudentModel2Serializer
else:
return StudentModelSerializer
# 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk
def get(self,request,pk):
"""获取一条数据"""
serializer = self.get_serializer(instance=self.get_object())
return Response(serializer.data)
def put(self,request,pk):
data = request.data
serializer = self.get_serializer(instance=self.get_object(),data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
serializer = self.get_serializer(instance=self.get_object())
return Response(serializer.data)
序列化器示例
from rest_framework import serializers
from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model= Student
fields = "__all__"
class StudentModel2Serializer(serializers.ModelSerializer):
class Meta:
model= Student
fields = ("name","class_null")
5个视图拓展类
- 提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量
- 这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法
ListModelMixin
- 列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码, 该Mixin的list方法会对数据进行过滤和分页
源码
class ListModelMixin(object):
"""
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)
示例
from rest_framework.mixins import ListModelMixin
from rest_framework.generics import GenericAPIView
class BookListView(ListModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request):
return self.list(request)
CreateModelMixin
- 创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码, 如果序列化器对前端发送的数据验证失败,返回400错误
源码
class CreateModelMixin(object):
"""
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 {}
示例
from rest_framework.mixins import CreateModelMixin
from rest_framework.generics import GenericAPIView
class Book(CreateModelMixin, GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
RetrieveModelMixin
- 详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象, 如果存在,返回200, 否则返回404
源码
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
# 获取对象,会检查对象的权限
instance = self.get_object()
# 序列化
serializer = self.get_serializer(instance)
return Response(serializer.data)
示例
from rest_framework.mixins import RetrieveModelMixin
from rest_framework.generics import GenericAPIView
class BookDetailView(RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
return self.retrieve(request)
UpdateModelMixin
更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象, 同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新, 成功返回200,序列化器校验数据失败时,返回400错误
源码
class UpdateModelMixin(object):
"""
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)
示例
from rest_framework.mixins import UpdateModelMixin
from rest_framework.generics import GenericAPIView
class BookDetail(UpdateModelMixin, GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
DestroyModelMixin
- 删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象, 成功返回204,不存在返回404
源码
class DestroyModelMixin(object):
"""
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()
示例
from rest_framework.mixins import DestroyModelMixin
from rest_framework.generics import GenericAPIView
class BookDetail(DestroyModelMixin, GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
混合使用示例 (创建、列表)
"""GenericAPIView结合视图扩展类实现api接口"""
from rest_framework.mixins import ListModelMixin,CreateModelMixin
class Students2GenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
# 本次视图类中要操作的数据[必填]
queryset = Student.objects.all()
# 本次视图类中要调用的默认序列化器[选填]
serializer_class = StudentModelSerializer
def get(self, request):
"""获取多个学生信息"""
return self.list(request)
def post(self,request):
"""添加学生信息"""
return self.create(request)
混合使用示例 (更新、删除)
from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student2GenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk
def get(self,request,pk):
"""获取一条数据"""
return self.retrieve(request,pk)
def put(self,request,pk):
"""更新一条数据"""
return self.update(request,pk)
def delete(self,request,pk):
"""删除一条数据"""
return self.destroy(request,pk)
GenericAPIView的9个视图子类
CreateAPIView
- 提供 post 方法
- 继承自: GenericAPIView、CreateModelMixin
ListAPIView
- 提供 get 方法
- 继承自:GenericAPIView、ListModelMixin
ListCreateAPIView
- 提供 get 和 post方法
- 继承自:GenericAPIView、ListModelMixin、CreateModelMixin
RetrieveAPIView
- 提供 get 方法
- 继承自: GenericAPIView、RetrieveModelMixin
DestoryAPIView
- 提供 delete 方法
- 继承自:GenericAPIView、DestoryModelMixin
UpdateAPIView
- 提供 put 和 patch 方法
- 继承自:GenericAPIView、UpdateModelMixin
RetrieveUpdateAPIView
- 提供 get、put、patch方法
- 继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
RetrieveDestroyAPIView
- 提供 get 和 delete 方法
- 继承自:GenericAPIView、RetrieveModelMixin、DestoryModelMixin
RetrieveUpdateDestoryAPIView
- 提供 get、put、patch、delete方法
- 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
CURD完整示例
import hashlib
from django.db import models
from django.conf import settings
from django.db import models
from django.contrib.auth.models import AbstractUser
table_prefix = "rest_"
class CoreModel(models.Model):
"""
核心标准抽象模型模型,可直接继承使用
增加审计字段, 覆盖字段时, 字段名称请勿修改, 必须统一审计字段名称
"""
id = models.BigAutoField(
primary_key=True,
help_text="Id",
verbose_name="Id"
)
description = models.CharField(
max_length=255,
verbose_name="描述",
null=True,
blank=True,
help_text="描述"
)
creator = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
related_query_name='creator_query',
null=True,
verbose_name='创建人',
help_text="创建人",
on_delete=models.SET_NULL,
db_constraint=False
)
modifier = models.CharField(
max_length=255,
null=True,
blank=True,
help_text="修改人",
verbose_name="修改人"
)
dept_belong_id = models.CharField(
max_length=255,
help_text="数据归属部门",
null=True,
blank=True,
verbose_name="数据归属部门"
)
update_datetime = models.DateTimeField(
auto_now=True,
null=True,
blank=True,
help_text="修改时间",
verbose_name="修改时间"
)
create_datetime = models.DateTimeField(
auto_now_add=True,
null=True,
blank=True,
help_text="创建时间",
verbose_name="创建时间"
)
class Meta:
abstract = True
verbose_name = '核心模型'
verbose_name_plural = verbose_name
class Users(CoreModel, AbstractUser):
GENDER_CHOICES = (
(0, "未知"),
(1, "男"),
(2, "女"),
)
USER_TYPE = (
(0, "后台用户"),
(1, "前台用户"),
)
username = models.CharField(
max_length=150,
unique=True,
db_index=True,
verbose_name="用户账号",
help_text="用户账号"
)
employee_no = models.CharField(
max_length=150,
unique=True,
db_index=True,
null=True,
blank=True,
verbose_name="工号",
help_text="工号"
)
email = models.EmailField(
max_length=255,
verbose_name="邮箱",
null=True,
blank=True,
help_text="邮箱"
)
mobile = models.CharField(
max_length=255,
verbose_name="电话",
null=True,
blank=True,
help_text="电话"
)
avatar = models.CharField(
max_length=255,
verbose_name="头像",
null=True,
blank=True,
help_text="头像"
)
name = models.CharField(
max_length=40,
verbose_name="姓名",
help_text="姓名"
)
gender = models.IntegerField(
choices=GENDER_CHOICES,
default=0,
verbose_name="性别",
null=True,
blank=True,
help_text="性别"
)
user_type = models.IntegerField(
choices=USER_TYPE,
default=0,
verbose_name="用户类型",
null=True,
blank=True,
help_text="用户类型"
)
last_token = models.CharField(
max_length=255,
null=True,
blank=True,
verbose_name="最后一次登录Token",
help_text="最后一次登录Token"
)
def set_password(self, raw_password, **kwargs):
super().set_password(
hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest()
)
class Meta:
db_table = table_prefix + "system_users"
verbose_name = "用户表"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Book(CoreModel):
title = models.CharField(
verbose_name="标题",
max_length=1024,
null=True,
blank=True,
help_text="标题",
)
author = models.CharField(
verbose_name="作者",
max_length=1024,
null=True,
blank=True,
help_text="作者",
)
class Meta:
db_table = table_prefix + "book"
verbose_name = "图书表"
verbose_name_plural = verbose_name
from myproject import models
from rest_framework.fields import empty
from rest_framework.request import Request
from rest_framework.serializers import ModelSerializer
from rest_framework.generics import CreateAPIView, ListAPIView, ListCreateAPIView
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView
class CustomModelSerializer(ModelSerializer):
"""
增强DRF的ModelSerializer,
(1)self.request能获取到rest_framework.request.Request对象
"""
def __init__(self, instance=None, data=empty, request=None, **kwargs):
super().__init__(instance, data, **kwargs)
self.request: Request = request or self.context.get("request", None)
class BookSerializer(CustomModelSerializer):
creator = UserSerializer(required=False)
create_datetime = serializers.DateTimeField(
format="%Y-%m-%d %H:%M:%S", required=False, read_only=True
)
update_datetime = serializers.DateTimeField(
format="%Y-%m-%d %H:%M:%S", required=False
)
# creator = serializers.StringRelatedField()
class Meta:
model = Book
fields = ["title", "author", "create_datetime", "update_datetime", "creator"]
depth = 1
# 此视图只可以新增、不可以查询
class Book(CreateAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
# 此视图查询一条、修改、删除都包含
class BookDetail(RetrieveUpdateDestroyAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
总结
两个基类
- APIView
- GenericAPIView: 数据库操作, queryset 和serializer_class
5个视图扩展类(rest_framework.mixins)
- CreateModelMixin: create方法创建一条
- DestroyModelMixin: destory方法删除一条
- ListModelMixin: list方法获取所有
- RetrieveModelMixin: retrieve获取一条
- UpdateModelMixin: update修改一条
9个子类视图(rest_framework.generics)
- CreateAPIView: 继承CreateModelMixin,GenericAPIView,有post方法,新增数据
- DestroyAPIView: 继承DestroyModelMixin,GenericAPIView,有delete方法,删除数据
- ListAPIView: 继承ListModelMixin,GenericAPIView,有get方法获取所有
- UpdateAPIView: 继承UpdateModelMixin,GenericAPIView,有put和patch方法,修改数据
- RetrieveAPIView: 继承RetrieveModelMixin,GenericAPIView,有get方法,获取一条
- ListCreateAPIView: 继承ListModelMixin,CreateModelMixin,GenericAPIView,有get获取所有,post方法新增
- RetrieveDestroyAPIView: 继承RetrieveModelMixin,DestroyModelMixin,GenericAPIView,有get方法获取一条,delete方法删除
- RetrieveUpdateAPIView: 继承RetrieveModelMixin,UpdateModelMixin,GenericAPIView,有get获取一条,put,patch修改
- RetrieveUpdateDestroyAPIView: 继承RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView,有get获取一条,put,patch修改,delete删除