当前位置 博文首页 > Silent丿丶黑羽:Django(55)GenericAPIView源码分析

    Silent丿丶黑羽:Django(55)GenericAPIView源码分析

    作者:Silent丿丶黑羽 时间:2021-06-09 18:25

    源码分析

    GenericAPIView继承自APIView,也就是在APIView基础上再做了一层封装,源码如下:

    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):
            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):
                queryset = queryset.all()
            return queryset
    
        def get_object(self):
            queryset = self.filter_queryset(self.get_queryset())
    
            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)
    
            self.check_object_permissions(self.request, obj)
    
            return obj
    
        def get_serializer(self, *args, **kwargs):
            serializer_class = self.get_serializer_class()
            kwargs.setdefault('context', self.get_serializer_context())
            return serializer_class(*args, **kwargs)
    
        def get_serializer_class(self):
            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__
            )
    
            return self.serializer_class
    
        def get_serializer_context(self):
            return {
                'request': self.request,
                'format': self.format_kwarg,
                'view': self
            }
    
        def filter_queryset(self, queryset):
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
    
        @property
        def paginator(self):
            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):
            if self.paginator is None:
                return None
            return self.paginator.paginate_queryset(queryset, self.request, view=self)
    
        def get_paginated_response(self, data):
            assert self.paginator is not None
            return self.paginator.get_paginated_response(data)
    

    我们可以看到GenericAPIView中定义了6个类属性和8个方法,接下来一个个分析
     

    类属性

    • 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
       

    queryset

    ??queryset是用来控制视图返回给前端的数据。如果没什么逻辑,可以直接写在视图的类属性中,如果逻辑比较复杂,也可以重写get_queryset方法用来返回一个queryset对象。如果重写了get_queryset,那么以后获取queryset的时候就需要通过调用get_queryset方法。因为queryset` 这个属性只会调用一次,以后所有的请求都是使用他的缓存。
     

    serializer_class

    ??serializer_class用来验证和序列化数据的。也是可以通过直接设置这个属性,也可以通过重写get_serializer_class来实现。
     

    lookup_field

    在检索的时候,根据什么参数进行检索。默认是pk,也就是主键。
     

    lookup_url_kwarg

    在检索的url中的参数名称。默认没有设置,跟lookup_field保持一致。
     

    filter_backends

    用于过滤查询集的过滤器后端类的列表。默认值与DEFAULT_FILTER_BACKENDS 设置的值相同。
     

    pagination_class

    当分页列出结果时应使用的分页类。默认值与 DEFAULT_PAGINATION_CLASS 设置的值相同,即 'rest_framework.pagination.PageNumberPagination'
     

    方法

    • get_queryset
    • get_object
    • get_serializer
    • get_serializer_class
    • get_serializer_context
    • filter_queryset
       

    get_queryset

    def get_queryset(self):
        # 断言queryset是否不为None
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )
        
        # 定义queryset属性,获取父类的queryset,如果父类没有定义类属性`queryset`,那么默认值就是None,就会报上面断言的错误 
        queryset = self.queryset
        # 如果queryset是QuerySet对象,那么返回全部内容
        if isinstance(queryset, QuerySet):
            queryset = queryset.all()
        # 如果不是queryset,那么直接返回
        return queryset
    

    get_queryset默认是返回数据库全部数据,如果想返回其他数据,需要自定义
     

    get_object

    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())
        
        # 查找过滤的条件,默认是pk
        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)
        
        # 可能会引发权限被拒绝
        self.check_object_permissions(self.request, obj)
    
        return obj
    

    该方法是用于在数据检索(通过pk查找)的时候,返回一条数据的。
     

    get_serializer

    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        kwargs.setdefault('context', self.get_serializer_context())
        return serializer_class(*args, **kwargs)
    

    返回应该用于验证和反序列化输入以及序列化输出的序列化器实例
     

    get_serializer_class

    def get_serializer_class(self):
        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__
        )
    
        return self.serializer_class
    

    返回用于序列化的类。默认使用self.serializer_class。如果您需要根据传入请求提供不同的序列化,您可能需要重写它。
     

    get_serializer_context

    def get_serializer_context(self):
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }
    

    提供给序列化的额外上下文。
     

    filter_queryset

    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset
    

    给定一个查询集,使用正在使用的过滤器对其进行过滤。您不太可能想要覆盖此方法,但如果您想将配置的过滤后端应用到默认查询集,您可能需要从列表视图或自定义get_object 方法中调用它。
     

    实战案例

    class StudentsGenericView(GenericAPIView):
        queryset = Student.objects.all()  # 定义了类属性queryset,告诉视图要针对哪个模型做处理
        serializer_class = StudentModelSerializer  # 定义类属性serlializer_class,告诉视图你的序列化的类是什么
        def get(self, request, *args, **kwargs):
            pk = kwargs.get("pk")
            if pk:
                many = False
                query = self.get_object()  # 通过pk检索数据,返回一条数据
            else:
                many = True
                query = self.get_queryset()  # 返回动态的数据集,默认返回全部
            serializer = self.get_serializer(query, many=many)  # 如果数据对象是queryset对象,many需要为True
            return APIResponse(results=serializer.data)
    
    bk