DRF:源码剖析 - 视图优化组件
简介及准备
django rest framework 给我们带来了很多组件,除了认证、权限、序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使用者而言不同的视图具有不同的功能,这样我们可以根据需求定制自己视图。以下是官网传送门:http://www.django-rest-framework.org/api-guide/views/
仍然以图书管理系统为例进行介绍
准备:
1、在settings 中注册app:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'apiview.apps.ApiviewConfig', 'serializer.apps.SerializerConfig', 'rest_framework' ]
2、models.py
from django.db import models # Create your models here. class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) # 外键字段(一对多关系) publish = models.ForeignKey(to="Publish", related_name="book", related_query_name="book_query", on_delete=models.CASCADE) # 多对多字段 authors = models.ManyToManyField(to="Author")
3、创建序列化类
同介绍序列化组件创建的类是一样的
from .models import ( Book, Publish, Author, ) from rest_framework import serializers class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ('title', 'price', 'publish', 'authors', 'author_list', 'publish_name', 'publish_city' ) # 返回给前端,显示的字段 extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True} } # 该字段只可写,不可读,不能够返回给前端 publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') # 在field中,必须声明出该字段publish_name,否则会报错 publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() # 多对多的关系,需要SerializerMethodField,并且用"get_字段名"方法,取值 def get_author_list(self, book_obj): # 拿到queryset开始循环 [{}, {}, {}, {}] authors = list() for author in book_obj.authors.all(): authors.append(author.name) return authors
视图组件的使用
视图组件是在视图函数中使用,views.py
由于前面学习的序列化组件,针对五种不同的接口,会有很多重复的代码,所以这里使用了视图组件来进行优化
一、第一次优化结果
获取所有数据、添加数据不需要传参,在一个视图函数内BookView
获取单条数据、删除数据、修改数据需要传参(id),在一个视图函数内BookFilterView
urls.py
from django.urls import path, include, re_path urlpatterns = [ re_path('serializer/', include('serializer.urls')) ]
from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$',views.BookView.as_view()), # 获取所有数据 添加数据 re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), # 删除 修改 获取单条数据 ]
views.py
from rest_framework.generics import GenericAPIView #基于GenericAPIView不再是APIView from rest_framework.mixins import ( ListModelMixin, # 获取所有数据 CreateModelMixin, # 添加数据
DestroyModelMixin, # 销毁删除数据 UpdateModelMixin, # 更新修改数据 RetrieveModelMixin # 获取单条数据 ) class BookView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = Book.objects.all() # 给定queryset, serializer_class = BookSerializer # 给定序列化的类,变量名是固定的 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 BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(self, request, *args, **kwargs): # print(self.kwargs) {'pk': '1'} return self.retrieve(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs)
二、再次优化结果
由于上述情况中,每种接口函数,都是类似的,有重复性,再次进行封装,如下
urls.py 同第一次优化的url一样
from django.urls import path, include, re_path urlpatterns = [ re_path('serializer/', include('serializer.urls')) ]
from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$',views.BookView.as_view()), # 获取所有数据 添加数据 re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), # 删除 修改 获取单条数据 ]
views.py
只需要给定queryset 和 序列化类即可
from rest_framework import generics # 全部封装到generics中 class BookView(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 变量名是固定的 class BookFilterView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer
三、最终优化结果
由于上述情况,两个视图函数中的代码重复,又进行了进一步的优化,不管是否传参,走不同的url,但是走同一个视图函数,将左右的内容封装到 ModelViewSet中
urls.py
from django.urls import path, include, re_path urlpatterns = [ re_path('serializer/', include('serializer.urls')) ]
from django.urls import re_path from serializer import views urlpatterns = [ #re_path(r'books/$', views.BookView.as_view()), #re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), re_path(r'books/$', views.BookView.as_view({ 'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ # 一定要使用命名路由,这样传参的时候才会关键字传参 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })) ]
views.py
from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet): # 不管是否传参,都走同一个视图函数
queryset = Book.objects.all() # queryset、serializer_class 是固定的字段名 serializer_class = BookSerializer
效果展示:
get请求,以获取所有数据为例,那么会返回数据是 [{},{},{},]

最终优化结果的扩展
该结果很简单,但是意味着不灵活,因此想要做更改,根据不同的请求,需要重写类中封装的方法(list、create、retrieve、update、destroy)
需求:前端有时候不仅需要数据,还需要返回一些状态码,这时候需要自定义response的字典,
以查看所有成绩的内容为例,重写 list 方法:
from .models import * from .utils.serializers import CourseSerializer from rest_framework.viewsets import ModelViewSet class CoursesView(ModelViewSet): queryset = Course.objects.all() # 以下两个变量名是固定的,指定queryset serializer_class = CourseSerializer # 指定序列化 def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) # 这两句都是固定的, serializer = self.get_serializer(queryset, many=True) response={} # 自定义返回的字典,并添加error_no、data键值 response["error_no"]=0 response["data"]=serializer.data # serializer.data就是之前返回的列表值,放到data中
return Response(response)
效果展示:
源码剖析
待补充

浙公网安备 33010602011771号