restframework 中的 Viewset 和 Serializer

2023年 1月 4日 39.5k 0

1. Django 中的 View Class

首先回忆一下,Django 对请求的处理逻辑。收到一次请求之后,Django 会生成一个 WSGIHandler 类型的 handler,由 handler 控制整个处理流程。那么,请求的 URL 与 View 是如何关联的呢?Django 首先根据 ROOT_URLCONF 的配置加载 URLconf,按顺序逐个匹配 URLconf 的 URLpatterns,匹配即停止。 然后,将 HttpRequest 对象作为参数,调用 URLpatterns 的 view 函数。再来看看,类视图路由的配置方法:

1
2
3
4
5
6
from django.conf.urls import url
from myapp import views

urlpatterns = [
    url(r'^fruit/', views.FruitView.as_view()),
]

django/views/generic/base.py 定义了 View 类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class View(object):
    @classonlymethod
    def as_view(cls, **initkwargs):
	    ...
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
		...
        return view
	    
	def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

通过调用 as_view() 方法,将返回 dispatch() 函数给路由函数。dispatch() 函数,会根据请求的方法,在 View 类中调用与请求方法同名的函数。这样,处理逻辑就非常清楚了。相较于函数视图,类视图多了 dispatch 这一步骤,可以理解为二次路由。

2. restframework 中的 View Class

restframwork 继承 Django View,实现了三个层级的 View,分别是 APIView、GenericAPIView、GenericViewSet。下面,我们来逐个分析。

2.1 APIView

rest_framewor/views.py 定义了 APIView 类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from rest_framework.request import Request

class APIView(View):
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
	...
	def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
	def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        try:
            self.initial(request, *args, **kwargs)
            handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

在 initial 函数中,会校验接口的版本、认证授权、权限验证、访问频率等。同时,restframework 对 request 进行了再次封装。在使用上 restframework 的 APIView 与 Django View 差别不大,但是对 View 提供的功能进行了增强。

2.2 GenericAPIView

rest_framework/generics.py 定义了 GenericAPIView 类:

1
2
3
4
5
6
7
8
9
class GenericAPIView(views.APIView):
    queryset = None
    serializer_class = None
    lookup_field = 'pk'
    lookup_url_kwarg = None
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    def get_queryset(self):
        ...

GenericAPIView 继承自 APIView,新增了一些类方法:

  • get_queryset,获取 QuerySet
  • get_object,获取单条数据
  • get_serializer,获取序列化后的数据
  • get_serializer_class,获取需要序列化的model类
  • get_serializer_context,获取序列化的数据,定义了某种格式的字典
  • paginator,分页器

除了直接继承 GenericAPIView 实现 View Class,restframework 还提供了大量 mixins。通过继承 mixins,可以快速组合需要的操作。请看下面这个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from rest_framework import mixins
from rest_framework import generics
class BookView(mixins.ListModelMixin,
               mixins.CreateModelMixin,
               generics.GenericAPIView):

    queryset = Book.objects.all()
    serializer_class = BookSerializers

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

更多 mixins:

mixins功能对应的 HTTP 请求方法
mixins.ListModelMixin 定义 list 方法,返回一个 querylist 的列表 GET
mixins.CreateModelMixin 定义 create 方法,创建一个示例 POST
mixins.RetrieveModelMixin 定义 retrieve 方法,返回一个具体的示例 GET
mixins.UpdateModelMixin 定义 update 方法,对某个实例进行更新 PUT/PATCH
mixins.DestroyModelMixin 定义 delete 方法,删除某个实例 DELETE

2.4 GenericViewSet

GenericAPIView 提供了增、删、改、查等基本操作,可以用于快速开发 API。但,如果需要对这些操作进行组合,实现复杂的业务逻辑时,可能就不那么方便了。幸运的是,restframework 提供了 GenericViewSet,用于更复杂的业务逻辑处理。rest_framework/viewsets.py 中定义了 GenericAPIView 类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class ViewSetMixin(object):
    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            return self.dispatch(request, *args, **kwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        view.suffix = initkwargs.get('suffix', None)
        view.actions = actions
        return csrf_exempt(view)
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    pass

内置的 ViewSet 有 5 种,分别是

  • ViewSetMixin
  • ViewSet
  • GenericViewSet
  • ReadOnlyModelViewSet
  • ModelViewSet

有两种方式,可以将 ViewSet 绑定到指定的 URL 上:

  • 手动将 ViewSet 绑定到 URL
  • 在 urls.py 中,新增:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    from .views import SnippetViewSet
    
    snippet_list = SnippetViewSet.as_view({
        'get': 'list',
        'post': 'create'
    })
    
    urlpatterns = [
        url(r'^snippets/$', snippet_list, name='snippet-list'),
    

    snippets/ 路径下,get 请求方法,分发到 list 函数;post 请求方法,分发到 create 方法。

  • 使用 restframework 提供的 routers
  • 在 urls.py 中,新增:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    from rest_framework.routers import DefaultRouter
    
    from .views import SnippetViewSet
    
    router = DefaultRouter()
    router.register(r'snippets', SnippetViewSet)
    urlpatterns = [
        url(r'^', include(router.urls)),
    ]
    

    3. Serializer

    restframework 中的 Serializer 和 ModelSerializer 与 Django 的 Form 和 ModelForm 类似。

    3.1 Serializer

    1
    2
    3
    4
    5
    
    from rest_framework import serializers
    
    class CommentSerializer(serializers.Serializer):
        email = serializers.EmailField()
        content = serializers.CharField(max_length=200)
    
    • 序列化:对象 -> Json
    1
    2
    3
    
    serializer = CommentSerializer(comment)
    serializer.data
    {'email': '[email protected]', 'content': 'foo bar'}
    
    • 反序列化:String -> Json
    1
    2
    3
    4
    5
    
    serializer = CommentSerializer(data=data)
    serializer.is_valid()
    True
    serializer.validated_data
    {'content': 'foo bar', 'email': '[email protected]'
    

    还可以用于验证,提交的数据是否符合合法:

    1
    2
    3
    
    serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            pass
    

    3.2 ModelSerializer

    相较与上面一个字段一个字段的声明,ModelSerializer 可以快速建立相关模型的 serializer 模型。提供如下特性:

    • 自动产生基于模型的 fileds
    • 自动产生验证器,比如 unique_together 验证器
    • 默认包含 create 和 update 方法
    • 外键被映射为 PrimaryKeyRelatedField
    1
    2
    3
    4
    5
    6
    
    from .models import Snippet
    
    class SnippetSerializer(serializers.ModelSerializer):
        class Meta:
            model = Snippet
            fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
    

    4. 参考

    • http://www.ziawang.com/article/302/
    • https://q1mi.github.io/Django-REST-framework-documentation/tutorial/quickstart_zh/
    • https://blog.csdn.net/l_vip/article/details/79131289
    • https://darkcooking.gitbooks.io/django-rest-framework-cn/content/chapter0.html
    • http://www.cnblogs.com/renpingsheng/p/7892719.html
    • https://whatwewant.gitbooks.io/django-rest-framework-tutorial-cn/content/1.Serialization.html

    相关文章

    KubeSphere 部署向量数据库 Milvus 实战指南
    探索 Kubernetes 持久化存储之 Longhorn 初窥门径
    征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
    那些年在 Terraform 上吃到的糖和踩过的坑
    无需 Kubernetes 测试 Kubernetes 网络实现
    Kubernetes v1.31 中的移除和主要变更

    发布评论