DRF源码剖析——视图

@

两个视图基类介绍
APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

相比View,主要多出来的功能有:

  • 视图方法可以返回rest_framework中的Response对象,APIView视图会为响应的数据渲染一个漂亮的前端页面,而不是仅仅返回字符串。
  • 会捕获APIException异常,基本所以的都会被捕获到,并且会帮我们处理成合适的响应信息;
  • APIView的dispatch()里面实现了像认证,权限,以及频率限制等其他功能。
GenericAPIView

简单使用示例

class BookGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
    lookup_field = 'pk'  # 默认就是pk,如果路由写正则的时候,用的是
    def get(self, request, *args, **kwargs):
        # 这里后面不用加.all(),因为源码内部帮我们加了
        # 群查
        book_query = self.get_queryset()  # 获取query数据
        book_ser = self.get_serializer(book_query, many=True)
        book_data = book_ser.data
        return APIResponse(results=book_data)

postman发送GET请求测试

GenericAPIView源码

先吃一份GenericAPIView的源码,关键部分已做了注释,

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    # You'll need to either set these attributes,
    # or override `get_queryset()`/`get_serializer_class()`.
    # If you are overriding a view method, it is important that you call
    # `get_queryset()` instead of accessing the `queryset` property directly,
    # as `queryset` will get evaluated only once, and those results are cached
    # for all subsequent requests.
    """
    下面这些是GenericAPIView的类属性
    我们继承GenericAPIView后,可以重写他们
    """
    # 指明视图需要的数据(从model中查询到的queryset对象)
    queryset = None
    # serializer_class指明视图使用的序列化器
    serializer_class = None

    # If you want to use object lookups other than pk, set 'lookup_field'.
    # For more complex lookup requirements override `get_object()`.
    # 自定义主键,默认是pk
    lookup_field = 'pk'
    lookup_url_kwarg = None

    # The filter backend classes to use for queryset filtering
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

    # The style to use for queryset pagination.
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        # get_serializer_class()返回serializer_class,没有写serializer_class会报错
        serializer_class = self.get_serializer_class()
        # get_serializer_context返回一个字典
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        # 先断言,如果serializer_class没有重写,就报错
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )
		# 返回serializer_class
        return self.serializer_class

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)

源码分析总结

GenericAPIView是继承APIView的,使用完全兼容APIView,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可以配合一个或多个Mixin扩展类
重点:GenericAPIView在APIView基础上完成了哪些事
 1)get_queryset():从类属性queryset中获得model的queryset数据                           群操作就走get_queryset()方法(包括群查,群增等)
 2)get_object():从类属性queryset中获得model的queryset数据,再通过有名分组pk确定唯一操作对象   单操作就走get_object()方法(包括单查,单增等)
 3)get_serializer():从类属性serializer_class中获得serializer的序列化类
五个视图扩展类

1.ListModelMixin(群查)

列表视图扩展类,提供 list 方法快速实现查询视图,返回200状态码。除了查询,该list方法会对数据进行过滤和分页

2.CreateModelMixin(单增) #注意:没有群增的方法,需要自己手动写(******)

创建视图扩展类,提供create方法快速创建资源的视图,成功返回201的状态码

3.RetrieveModelMixin(单查)

详情视图扩展类,提供retrieve方法,可以快速实现返回一个存在的数据对象。

4.UpdateModelMixin(更新,修改) #只有单整体改和单局部改,没有群整体改和群局部改

更新视图扩展类,提供update方法,可以快速实现更新一个存在的数据对象,同时也提供partial_update方法,可以实现局部更新。

5.DestoryModelMixin(删除) 一般不怎么用到

删除视图扩展类,提供destory方法,可以快速实现删除一个存在数据对象。

视图扩展类使用示例

class BookMixinGenericAPIView(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, CreateModelMixin, GenericAPIView):
    # 调用工具类的时候会去找serializer_class等,所以得先定义
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
    lookup_field = 'pk'
    def get(self, request, *args, **kwargs):
        if request.GET.get('pk'):
            response = self.retrieve(request, *args, **kwargs)  # 单查,使用RetrieveModelMixin
        else:
            # 群查
            response = self.list(request, *args, **kwargs)
        return APIResponse(results=response.data)   # 记得加data吧序列化后的数据拿出来
    # 单增
    def post(self, request, *args, **kwargs):
        response =  self.create(request, *args, **kwargs)
        return APIResponse(results=response)
    # 单整体修改
    def put(self, request, *args, **kwargs):
        response = self.update(request, *args, **kwargs)
        return APIResponse(results=response)
    # 单局部修改
    def patch(self, request, *args, **kwargs):
        response = self.partial_update(request, *args, **kwargs)
        return APIResponse(results=response)
常用功能子类视图(工具视图)

工具视图(继承了GenericAPIView和各种Mixins工具类)

  • 1)工具视图都是GenericAPIView的子类,并且不同的子类继承了不同的工具类去完成不同的功能
  • 2)工具视图的功能可以满足需求,只需要继承工具视图,并且提供queryset与serializer_class即可
  • 3 ) 其实这些东西就是想帮我们省代码,因为Django的目标就是为开发者实现快速开发,是一个重量级框架

在这里插入图片描述

使用示例

我们不用再自己写get(),put()等方法,因为DRF源码里面已经帮我们做了。

from rest_framework.generics import ListCreateAPIView, UpdateAPIView
# 基于常用功能子类视图实现群查,新增/更改一条记录
class BookListCreateAPIView(ListCreateAPIView, UpdateAPIView):
    serializer_class = serializers.BookModelSerializer
    queryset = models.Book.objects.filter(is_delete=False)
源码分析

先来一份源码,因为这里很简单,我就不注释了。

class CreateAPIView(mixins.CreateModelMixin,
                    GenericAPIView):
    """
    Concrete view for creating a model instance.
    """
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)


class RetrieveAPIView(mixins.RetrieveModelMixin,
                      GenericAPIView):
    """
    Concrete view for retrieving a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)


class DestroyAPIView(mixins.DestroyModelMixin,
                     GenericAPIView):
    """
    Concrete view for deleting a model instance.
    """
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class UpdateAPIView(mixins.UpdateModelMixin,
                    GenericAPIView):
    """
    Concrete view for updating a model instance.
    """
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)


class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
    """
    Concrete view for listing a queryset or creating a model instance.
    """
    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 RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
                            mixins.UpdateModelMixin,
                            GenericAPIView):
    """
    Concrete view for retrieving, updating a model instance.
    """
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)


class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
                             mixins.DestroyModelMixin,
                             GenericAPIView):
    """
    Concrete view for retrieving or deleting a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
    """
    Concrete view for retrieving, updating or deleting a model instance.
    """
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

源码分析总结:

工具视图在源码里边的实现是:继承GenericAPIView,以及各种Mixin工具类,然后去实现增删改查方法,也就是那几行增删改查的方法都帮我们实现了,如果工具视图可以满足我们的需求的话,我们只需要继承对应的工具视图类就行了。

原文地址:https://www.cnblogs.com/happystruggle/p/12678115.html