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)

效果展示:

            

 

源码剖析

待补充

 

posted @ 2018-12-10 19:46  葡萄想柠檬  Views(183)  Comments(0)    收藏  举报
目录代码