学畅留学招聘网站开发主管,手机个人简历模板下载网站模板,濮阳网站关键词,抖音做我女朋友的网站本文将介绍django视图集的内部实现#xff0c;并带你重写部分代码自己组装强大且趁手的视图集#xff0c;以满足自定义的业务需求#xff0c;避免编写大量重复代码。
一、基础知识
Django Rest framework框架允许你将一组相关视图的逻辑组合到一个类中#xff0c;也就是我…本文将介绍django视图集的内部实现并带你重写部分代码自己组装强大且趁手的视图集以满足自定义的业务需求避免编写大量重复代码。
一、基础知识
Django Rest framework框架允许你将一组相关视图的逻辑组合到一个类中也就是我们所谓的视图集ViewSet。
与APIView相比ViewSet更加抽象更难理解。APIView的使用方法非常直观需要你提供诸如.get()或.post()之类的处理方法并由其自动将请求分发到对应的方法上。ViewSet则不同它要求你提供诸如.list()或.create()这类操作方法在实现了这些方法之后使用.as_view()方法请操作方法映射到不同的处理方法上比如list-get、destroy-delete。
一个简单的示例
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Responseclass UserViewSet(viewsets.ViewSet):一个简单的视图集实现获取用户列表和查询单个用户的功能def list(self, request):queryset User.objects.all()serializer UserSerializer(queryset, manyTrue)return Response(serializer.data)def retrieve(self, request, pkNone):queryset User.objects.all()user get_object_or_404(queryset, pkpk)serializer UserSerializer(user)return Response(serializer.data)在很多普通业务中增删改查的操作是相同的比如请求数据列表的操作无非是三步数据库获取查询集-序列化-返回响应。drf预制了这些重复的工作将通用的方法封装进了ModelViewSet借助ModelViewSet我们可以非常轻松的完成增删改查等工作(对应APIView就是ModelAPIView)
class AccountViewSet(viewsets.ModelViewSet):只需要指定查询集和序列化器即可queryset Account.objects.all()serializer_class AccountSerializerpermission_classes [IsAccountAdminOrReadOnly]ModelViewSet本身不提供这些通用的list或create之类的方法而是由一些列Mixin类来实现ModelViewSet负责把它们组合起来:
class ModelViewSet(mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet): pass就以CreateModelMixin为例CreateModelMixin为我们提供了一个通用的和符合标准的create()方法其过程也就是获取查询集-序列化-返回没有特殊需求我们的视图集继承它就能获取预制的create方法不需要再自己实现
class CreateModelMixin:Create a model instance.def create(self, request, *args, **kwargs):serializer self.get_serializer(datarequest.data)serializer.is_valid(raise_exceptionTrue)self.perform_create(serializer)headers self.get_success_headers(serializer.data)return Response(serializer.data, statusstatus.HTTP_201_CREATED, headersheaders)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 {}细心观察在CreateModelMixin中我们获取序列化器的方法是get_serializer此外一些其它的Minxin类中你可能发现其获取查询集的方法是get_queryset或者filter_queryset还有诸如paginate_queryset这样的方法一个典型的示例就是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, manyTrue)return self.get_paginated_response(serializer.data)serializer self.get_serializer(queryset, manyTrue)return Response(serializer.data)这些方法并非凭空而来而是由GenericViewSet类来提供准确说是它的父类GenericAPIView
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): passGenericViewSet继承GenericAPIView为各种Mixin提供统一的获取查询集、序列化器、分页、授权等等接口。
class GenericAPIView(views.APIView):...queryset Noneserializer_class None...def get_queryset(self):...def get_object(self):...def get_serializer(self, *args, **kwargs):...def get_serializer_class(self):...def get_serializer_context(self):...def filter_queryset(self, queryset):...def paginator(self):...def paginate_queryset(self, queryset):...def get_paginated_response(self, data):...二、灵活自定义
drf预制的Mixin足够标准和通用但如果我们的业务中有特殊需求我们就需要对drf预制的Mixin重新烹饪实际操作并不困难接下来我们通过几个具体的场景来实际体会一下。
自定义响应格式
假如我想让视图集返回的响应遵循如下格式
{status: ok,code: 200,messages: [],result: {user: {id: 123,name: shazow}}
}我们可以先实现一个自定义的响应类来替换掉Mixin中使用的响应类。
import json
from rest_framework.response import Responseclass Rep(Response):struct json responsedef __init__(self, resultNone, messageNone, statusNone, codeNone, **kwargs):if message is None:message []data {status: status,code: code,message: message,result: result}super().__init__(data, code, **kwargs)staticmethoddef ok(resultNone, messageNone, codeNone, **kwargs):return Rep(resultresult, messagemessage, statusok, codecode, **kwargs)staticmethoddef err(resultNone, messageNone, codeNone, **kwargs):return Rep(resultresult, messagemessage, statuserr, codecode, **kwargs)以RetrieveModelMixin为例你可以继承并重写retrieve也可以干脆复制一份到自己的项目中再修改retrieve方法我们这里选择复制一份到自己的项目中。为了和原来的RetrieveModelMixin做区分且将其命名为XRetrieveModelMixin:
class XRetrieveModelMixin:Retrieve a model instance.# 使用我们自己的Rep响应类替换了Response响应类def retrieve(self, request, *args, **kwargs):try:instance self.get_object()except Http404:return Rep.err(None, [查询数据不存在], status.HTTP_404_NOT_FOUND)serializer self.get_serializer(instance)return Rep.ok(serializer.data, None, codestatus.HTTP_200_OK)# 对比原来的
# 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)自动记录创建和更新数据的用户
细心观察drf的Minxin类并不是将全部分逻辑写在一个create方法或者update方法中实际上它把实现功能的代码拆分到了多个函数中。
以CreateModelMixin类为例你可以看到create的方法由perform_create方法和get_success_headers组合而来
class CreateModelMixin:Create a model instance.def create(self, request, *args, **kwargs):serializer self.get_serializer(datarequest.data)serializer.is_valid(raise_exceptionTrue)self.perform_create(serializer)headers self.get_success_headers(serializer.data)return Response(serializer.data, statusstatus.HTTP_201_CREATED, headersheaders)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 {}这样非常有利于我们进行重写假如我们想对serializer.save()的过程做些修改比如记录创建用户我们就可以通过重写perform_create来实现
class TrackerModelViewSet(ModelViewSet):def perform_create(self, serializer):serializer.save(created_byself.request.user)# 记录更新操作的用户perform_update来自UpdateModelMixindef perform_update(self, serializer):serializer.save(updated_byself.request.user)三、总结
学习drf是如何预制Mixin的我们可以预制自己的Mixin类和视图集运用得当我们可以打造属于自己的趁手工具以从大量重复工作中解脱。