美文网首页Django
django rest framework 视图源码解析

django rest framework 视图源码解析

作者: 程序员同行者 | 来源:发表于2018-11-04 16:12 被阅读0次

视图源码解析

image image

1) CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
3)RetireveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

下面就一个个来介绍:

Mixins的五个类:

首先点开ModelViewSet类:

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):

点进第一个类中:

CreateModelMixin:
class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

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

这是CreateModelMixin类下的源码,首先通过request获取请求得到的信息传给参数serializer,对它进行用户Token验证,如果用户通过,将其保存并且通过return {‘Location’: str(data[api_settings.URL_FIELD_NAME])}保存一个临时头部数据,最后将序列化的数据,响应状态码(201 Created:请求已经被实现)、头部一并返回即可。
另外补充,get_serializer方法是继承自GenericAPIView类的序列化数据。

RetrieveModelMixin:
class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

这是RetrieveModelMixin下的源码,首先同样get_object和get_serializer是继承自GenericAPIView类中的方法,这里需要注意一下get_object的这条语句:

lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

因为这个函数后面部分都是对lookup_url_kwarg这个参数进行处理,而给这个参数赋值的两个,前者在最前面的初始参数是None,所以我们又跳到后者lookup_field,发现它的默认值是"pk",所以这也是为什么我们之前只要是精确查找路由部分正则后的参数都设为pk的原因。

  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

总结:返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在视图中可以调用该方法获取详情信息的模型类对象。若详情访问的模型类对象不存在,会返回404。该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。

  1. lookup_field 查询单一数据库对象时使用的条件字段,默认为’pk’
  2. lookup_url_kwarg 查询单一数据时URL中的参数关键字名称,默认与look_field相同
UpdateModelMixin
class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

和前面的create类似,意思通过instance = self.get_object()获取要修改的数据,然后get_serializer拿到序列化器、进行请求数据合法校验、然后通过Token进行用户验证、然后将更新好的数据返回return Response(serializer.data)

DestroyModelMixin
class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

通过pk(或者其他标识)获取要删除的数据,然后instance.delete()删除,最后返回return Response(status=status.HTTP_204_NO_CONTENT)
HTTP 204(no content)表示响应执行成功,但没有数据返回,浏览器不用刷新。这个除了调用get_object方法获取数据,其它都在类范围内。

ListModelMixin
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)

queryset = self.filter_queryset(self.get_queryset()),ListModelMixin类list方法获取了这个queryset,同样该方法是在GenericAPIView类中,然后在判断是否分页,最后获取self.get_serializer(queryset, many=True)序列化类,最后将序列化后的结果serializer.data作为返回return Response(serializer.data)返回即可。这里就不再分析中间这段分页的含义了,分页也算一个组件,如果以后有时间,可以再开一贴分析一下,我们只要看懂前面和后面就行了。

五个类小结

其实我们发现上面这五个类进行的功能并不多,只是说帮我们省略了逻辑处理部分,还有解决了一些零零碎碎的小问题,那么我们定义的路由,以及视图部分定义的类,怎样才能让django识别到,并且既然是CBV,那么就少不了as_view()方法,那么它在哪,下面就让我们进入GenericViewSet下

GenericViewSet
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

直接进入继承类。

ViewSetMixin

我们先进入ViewSetMixin模块类中,发现它类下的第一个便是as_view方法,然后再进一步可以看到view函数,我们此处需要的就是它:

def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    # We also store the mapping of request methods to actions,
    # so that we can later set the action attribute.
    # eg. `self.action = 'list'` on an incoming GET request.
    self.action_map = actions

    # Bind methods to actions
    # This is the bit that's different to a standard view
    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

    self.request = request
    self.args = args
    self.kwargs = kwargs

    # And continue as usual
    return self.dispatch(request, *args, **kwargs)
 for method, action in actions.items():
        handler = getattr(self, action)
        setattr(self, method, handler)

这一段的意思是遍历actions里的数据,method接收的是字典的键,而action接收的是值,actions就是我们在url传递参数,然后通过getattr反射将action的值给handler,最后执行setattr,这个方法的意思是给对象的属性赋值,若属性不存在,先创建再赋值。也就是说将method方法里的替换成handler。

APIView

之后找到我们想要定义的dispatch方法的回调函数里,这个是在APIView模块类中,并且算是重写了dispatch方法,如果还想刨根揭底的话,就会发现它后面还有一个dispatch方法,是View模块下的,这个可以看上面我画的流程图。所以dispatch方法的返回值,就是view的返回值,view的返回值就是as_view的返回值,那么我们整个源码逻辑就都通了。

相关文章

网友评论

    本文标题:django rest framework 视图源码解析

    本文链接:https://www.haomeiwen.com/subject/sfelxqtx.html