欢迎来到夜的世界

莫听穿林打叶声,何妨吟啸且徐行。竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生.料峭春风吹酒醒,微冷,山头斜照却相迎。回首向来萧瑟处,归去,也无风雨也无晴。
扩大
缩小

序列化组件(get/put/delete接口设计),视图优化组件

      一 . 知识点回顾        

1 . 混入类 , 多继承 

class Animal(object):
    def eat(self):
        print("Eat")
        
    def walk(self):
        print("Walk")
        
    def sleep(self):
        print("Sleep")
        
    def run(self):
        print("Run")

    def flying(self):
        print("Flying")
        
    def wangwang(self):
        print("Wangwang")
        
    def miao(self):
        print()


class Dog(Animal):pass


class Cat(Animal):pass


class Bird(Animal):pass

可以看到,Dog类继承了Animal类,但是Dog并没有飞行和喵的功能,所以,如果直接继承Animal会有一些问题,请同学们思考,如何解决这个问题呢?

好了,其实我们有多中方式可以解决这个问题,比如:

class Animal(object):
    def eat(self):
        print("Eat")

    def walk(self):
        print("Walk")

    def sleep(self):
        print("Sleep")

    def run(self):
        print("Run")


class Flying(object):
    def flying(self):
        print("Flying")


class WangWang(object):
    def wangwang(self):
        print("Wangwang")


class Miao(object):
    def miao(self):
        print()


class Dog(Animal, WangWang):pass


class Cat(Animal, Miao):pass


class Bird(Animal, Flying):pass

我们将不同的功能封装到了独立的类中,然后采用一种Mixin(混合类)的方式,其实也就是多继承,来解决这个问题,这在Python中是比较常见的解决方式,比如socketserver模块就用到了这种方式,当我们需要线程的时候,可以继承线程类,当我们需要进程的时候,可以继承进程类。

2 . 函数的参数 

def func(a, b, c=1, *args, **kwargs):
    print(a, b, c, args, kwargs)

func(1, 2, 3, 4, 5)                # 1, 2, 3, (4, 5) 所有未被匹配到的非key=value型的参数都会被*args接收
func(b=1, a=1, c=2, 4, 5)          # 报错,位置参数不能在关键字参数之后
func(4, 5, c=3, {"name": "pizza"}) # 报错,位置参数不能在关键字参数之后,字典不是关键字参数
func({"name": "pizza"}, 1, 2)      # {'name': 'pizza'} 1 2 () {}
func(1, 2, 3, 4, 5, d=1, e=2)      # 1 2 3 (4, 5) {'d': 1, 'e': 2} 未被匹配到的关键字参数被传递给kwargs

      二 . 序列化组件(get/put/delete接口设计)       

   基于上篇的表结构,通过序列化组件的 ModelSerializer 设计如下三个接口 : 

GET       127.0.0.1:8000/books/{id}    # 获取一条数据,返回值:{}
PUT       127.0.0.1:8000/books/{id}    # 修改数据,返回值:{}
DELETE    127.0.0.1:8000/books/{id}    # 删除数据,返回空

urls.py 文件

from django.urls import path, re_path
from serializer import views
urlpatterns = [
    re_path('books/(\d+)/$', views.BookFilterView.as_view())
]

 Views,py 文件 :

class BookFilterView(APIView):
    def get(self, request, nid):
        book_obj = Book.objects.get(pk=nid)
        serialized_data = BookSerializer(book_obj, many=False)
        return Response(serialized_data.data)

    def put(self, request, nid):
        book_obj = Book.objects.get(pk=nid)
        verified_data = BookSerializer(data=request.data, instance=book_obj, many=False)
        if verified_data.is_valid():
            verified_data.save()
            return Response(verified_data.data)
        else:
            return Response(verified_data.errors)

    def delete(self, request, nid):
        Book.objects.get(pk=nid).delete()
        return Response()

PUT接口逻辑的设计,分为如下几个步骤:

  • url设计:re_path(r’books/(\d+)/$’, views.BookFilterView.as_view())
  • 视图类:重新定义一个视图类
  • put方法:在视图类中定义一个put方法
  • 序列化:在序列化的过程中,需要传入当前修改的数据行,参数名为instance
  • 序列化类:不需要修改
  • url路径:请求时,发送的url必须与urls.py中定义的url完全匹配

 所以 , 加上上一篇的get和post接口,我们现在5个常用接口设计完成了,接口如下 : 

class BookView(APIView):
    def get(self, request):
        origin_books = Book.objects.all()
        serialized_books = BookSerializer(origin_books, many=True)

        return Response(serialized_books.data)

    def post(self, request):
        verified_data = BookSerializer(data=request.data)

        if verified_data.is_valid():
            book = verified_data.save()
            return Response(verified_data.data)
        else:
            return Response(verified_data.errors)


class BookFilterView(APIView):
    def get(self, request, nid):
        book_obj = Book.objects.get(pk=nid)

        serialized_data = BookSerializer(book_obj, many=False)

        return Response(serialized_data.data)

    def delete(self, request, nid):
        book_obj = Book.objects.get(pk=nid).delete()

        return Response("")

    def put(self, request, nid):
        book_obj = Book.objects.get(pk=nid)

        serialized_data = BookSerializer(data=request.data, instance=book_obj)

        if serialized_data.is_valid():
            serialized_data.save()
            return Response(serialized_data.data)
        else:
            return Response(serialized_data.errors)
两个视图类的接口逻辑

 

         三 . 视图组件       

   前面的介绍中,我们已经通过序列化组件设计除了符合REST规范的五个常用接口,现在假设,我们有多个数据接口,比如(Book,Author,Publish...)等数据表都需要定义类似的接口,可以预见,我们需要重复定义类似上面的五个接口,这种方式将会导致大量的重复代码,显然,我们的程序还有很多需要优化的地方,那么,如果是你,将会如何进行优化呢?

   这个时候,就需要我们的视图组件进行优化了!!!!!!

  视图组件是用来优化接口逻辑的

      视图组件的使用      

 1 . 使用视图组件的 mixin 进行接口逻辑优化 : 

urls.py 代码 : 

from django.urls import re_path
from serializer import views

urlpatterns = [
    re_path('books/$', views.BookView.as_view()),
    re_path('books/(?P<pk>\d+)/$', views.BookFilterView.as_view()),
]

 视图 views.py : 

from rest_framework.mixins import (
    ListModelMixin,
    CreateModelMixin,
    DestroyModelMixin,
    UpdateModelMixin,
    RetrieveModelMixin
)
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
# 导入序列化类
from .app_serializers import BookSerializer
from .models import Book, Publish, Author

class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
    # queryset和serializer_class是固定写法
    queryset = Book.objects.all()
    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, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    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 delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

   注意:单条数据操作的url有变化,因为我们在视图中,统一传的都是queryset,所以,需要通过传入一个名为pk的命名参数,告诉视图组件,用户需要操作的具体数据。

 

2 . 使用视图组件的 genericview 进行接口逻辑优化 

  上面的代码看似已经优化的非常完美了,但是,在一个对性能要求极高的项目里面,我们的程序还可以继续优化,不断优化程序是每个程序员必备的技能,也是帮助我们成长的重要手段。同样的思路,同样的方法,我们可以将多个接口封装到一个功能类中,如下代码:

 views.py : 

from rest_framework import generics

from .app_serializers import BookSerializer
from .models import Book, Publish, Author

class BookView(generics.ListCreateAPIView):
     queryset = Book.objects.all()
     serializer_class = BookSerializer

class BookFilterView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

 

3 . 使用视图组件的 viewset 进行接口逻辑优化 

  上面的代码已经看似非常完美了,但是,你有没有发现还有重复代码,该如何改进呢?使用viewset可以进一步优化,如下:

urls.py文件(注意跟之前有什么不同)

from django.urls import re_path
from serializer import views

urlpatterns = [
    re_path('books/$', views.BookView.as_view({
        'get': 'list',
        'post': 'create'
    })),
    re_path('books/(?P<pk>\d+)/$', views.BookView.as_view({
        'get': 'retrieve',
        'put': 'update',
        'delete': 'destroy'
    }))

 视图 views.py : 

from rest_framework.viewsets import ModelViewSet
from .app_serializers import BookSerializer
from .models import Book, Publish, Author

class BookView(ModelViewSet):
     queryset = Book.objects.all()
     serializer_class = BookSerializer

   使用方式是不是很简单,接下来去看以下源码都为我们做了什么吧!其实整个viewset优化方案最重要的地方就是urls.py中传入了参数,然后对参数进行映射关系绑定。

 

        viewset 源码剖析    

  • Django程序启动,开始初始化,读取urls.py, 读取settings, 读取视图类
  • 执行as_views(), BookView没有,需要到父类(ModelViewSet)中找
  • ModelViewSet继承了mixins的几个ModelMixin和GenericViewSet,显然ModelMixin也没有,只有GenericViewSet中有
  • GenericViewSet没有任何代码,只继承了ViewSetMixin和generics.GenericAPIView(这个我们已经认识了)
  • 继续去ViewSetMixin中查找,找到了as_view类方法,在重新封装view函数的过程中,有一个self.action_map = actions
  • 这个actions就是我们给as_view()传递的参数
  • 绑定url和视图函数(actions)之间的映射关系
  • 等待用户请求
  • 接收到用户请求,根据url找到视图函数
  • 执行视图函数的dispatch方法(因为视图函数的返回值是:return self.dispatch()
  • dispatch分发请求,查找到视图类的五个方法中的某个
  • 开始执行,比如post请求,返回:self.create(),视图类本身没有,则会到父类中查找
  • 最后在CreateModelMixin中查找
  • 执行create()方法,获取queryset和serializer_class
  • 返回数据

这就是viewset的优化方案,整个优化方案最重要的地方就是urls.py中我们传入的参数,然后对参数进行映射关系绑定。

 

posted on 2018-12-09 13:45  二十四桥_明月夜  阅读(374)  评论(0编辑  收藏  举报

导航