rest framework 之视图

Posted midworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rest framework 之视图相关的知识,希望对你有一定的参考价值。

一、APIView

APIView 直接继承 View(Django 内置的 View),也就是说 APIView 是最贴近原生 Django 的 View 的。

因此可定制程度高,根据请求方法不同执行不同的函数:

源码:

class APIView(View):

    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    # Allow dependency injection of other settings to make testing easier.
    settings = api_settings

    schema = DefaultSchema()

    @classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        ...

使用方法

1、urls.py

from django.urls import path, re_path
from api.views import UserView

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/users/', UserView.as_view(), name='api_user'),
]

2、views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView


class UserView(APIView):

    def get(self, request, *args, **kwargs):
       pass

    def post(self, request, *args, **kwargs):
        pass

二、GenericViewSet

映射关系

GenericViewSet 在 URL 中采用映射的关系将请求方法于视图函数一一对应,具体如下图:

URL 参数与视图函数映射关系

参数 视图中方法 说明
get list 获取数据
post create 创建数据
get retrieve 获取单条数据
put update 更新
patch partial_update 局部更新
delete destroy 删除

当然你也可以定位别的函数名,比如更新使用 add,相应地视图中也要定义为 def add() pass

在使用时,URL 中对应了几组映射关系,相应地视图中也要重写相应函数/方法。


GenericViewSet 继承关系

GenericViewSet 继承 GenericAPIView

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

GenericAPIView 中实现了以下方法,包括:获取数据源、序列化、分页等

技术图片

使用时,我们只需定义传入数据源、序列化的类和分页的类即可:

from rest_framework.viewsets import GenericViewSet
from .serializers import TestSerializers
from rest_framework.pagination import PageNumberPagination


class TestView(GenericViewSet):
    queryset = models.Role.objects.all()
    serializer_class = TestSerializers
    pagination_class = PageNumberPagination

    def list(self, request, *args, **kwargs):
        # 获取数据
        roles = self.get_queryset()
    
        # 分页
        pager_roles = self.paginate_queryset(roles)
    
        # 序列化
        roles_ser = self.get_serializer(instance=pager_roles, many=True)
    
        return Response(roles_ser.data)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        # 验证用户输入的数据是否合法
        serializer.is_valid(raise_exception=True)
        # 合法则保存
        serializer.save()

        # headers
        # headers = self.get_success_headers(serializer.data)

        # 创建成功则返回刚刚创建的数据
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)


    def get_success_headers(self, data):
        try:
            return 'Location': str(data[api_settings.URL_FIELD_NAME])     # URL_FIELD_NAME:url
        except (TypeError, KeyError):
            return 

技术图片

其中 get_queryset()paginate_queryset()get_serializer() 都是 GenericViewSet 实现的方法,直接调用即可。


如果你嫌每次还要写 list、create、delete 等方法,也可以直接继承 mixins.ListModelMixin、 mixins.CreateModelMixin、mixins.DestroyModelMixin ,其内部以及帮你实现了增删改查等功能,使用时只需继承即可。

from rest_framework.viewsets import GenericViewSet
from .serializers import TestSerializers
from rest_framework.pagination import PageNumberPagination
from rest_framework import mixins


class TestView(mixins.ListModelMixin, mixins.CreateModelMixin,  GenericViewSet):
    # 只需将数据源于序列化的类和分页类赋值即可
    queryset = models.Role.objects.all()
    serializer_class = TestSerializers
    pagination_class = PageNumberPagination

源码分析

ListModelMixin 来看,是不是和我们之前自定义的 list 做的事一样,同样地它也是实现了查询数据、序列化和分页的功能:

class ListModelMixin(object):
    """
    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, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

更多源码可参考:rest_framework/mixins.py

技术图片


参考

1、urls.py

from django.urls import path, re_path
from api.views import TestView

urlpatterns = [
    re_path('(?P<version>[v1|v2]+/test/)', TestView.as_view('get': 'list', 'post': 'create')),
]

2、models.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework import viewsets
from rest_framework.response import Response


class TestView(viewsets.GenericViewSet):
    def list(self, request, *args, **kwargs):
        return Response('...')

    def add(self, request, *args, **kwargs):
        pass

    # def delete(self, request, *args, **kwargs):
    #     pass

    # def edit(self, request, *args, **kwargs):
    #     pass

技术图片


权限

class GenericAPIView(views.APIView):
    """返回请求的对象"""
    def get_object(self):
        ...
        # May raise a permission denied
        self.check_object_permissions(self.request, obj)
        ...
        pass
def check_object_permissions(self, request, obj):
    """
    Check if the request should be permitted for a given object.
    Raises an appropriate exception if the request is not permitted.
    检查是否应该允许给定对象的请求。
????如果不允许请求,则引发适当的异常。
    """
    for permission in self.get_permissions():
        if not permission.has_object_permission(request, self, obj):
            self.permission_denied(
                request, message=getattr(permission, 'message', None)
            )

三、ModelViewSet

ModelViewSet 是目前 rest_framework 中最高级别的 View,它继承了:

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

因此只要继承 ModelViewSet 即可完成增删改查。

1、urls.py

from django.urls import path, re_path
from api.views import TestView, TestView1

urlpatterns = [
    re_path('(?P<version>[v1|v2]+/test/)', TestView.as_view('get': 'list', 'post': 'create')),

    re_path('(?P<version>[v1|v2]+/test1/)', TestView1.as_view('get': 'list', 'post': 'create')),

    #  re_path('(?P<version>[v1|v2]+/test1/(?P<pk>\d+)/)', TestView1.as_view('get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy')),
]

2、views.py

from rest_framework.viewsets import GenericViewSet
from .serializers import TestSerializers
from rest_framework.pagination import PageNumberPagination
from rest_framework.viewsets import ModelViewSet


class TestView1(ModelViewSet):
    queryset = models.Role.objects.all()
    serializer_class = TestSerializers
    pagination_class = PageNumberPagination

四、总结

  • 如果想快速实现增删改查:ModelViewSet
  • 增删:CreateModelMixinDestroyModelMixinGenericViewSet
  • 复杂逻辑:GenericViewSetAPIView

以上是关于rest framework 之视图的主要内容,如果未能解决你的问题,请参考以下文章

rest framework 之视图

python-django rest framework框架之视图

django-rest-framework框架总结之View视图之APIViewGenericAPIView视图集ViewSet

DRF:如何将 django-rest-framework-jwt 集成到 Djoser

rest_framework视图基类与五个扩展类

rest-framework之权限组件