drf的GenericAPIView

64 阅读4分钟

学习原因:
关于GenericAPIView,这应该也是一个写接口的东西,我们知道,之前我们一直用APIview来写接口,但是我们会发现,当我们写很多接口的时候,很多代码会重复,说白了,就改个别地方,我来举个例子来帮助理解,我们在学习这种继承类的时候,他会保留原来的功能,增加新的功能,但是这种高集成的类有好处也有坏处。

好处:写代码更加便捷,不会有很多的冗余代码

坏处:你写代码的灵活度更低了,没有那么的灵活多变了

先把代码展示一下.....

class Bookview(GenericAPIView):
    queryset = UserInfo.objects.all()
    serializer_class=UserinfoSerializers
    def get(self,request):
        serializers = self.get_serializer(instance=self.get_queryset(), many=True)
        return Response(serializers.data)

    def post(self, request):
        serializers=self.get_serializer(data=request.data)
        # 校验数据
        if serializers.is_valid():
            serializers.save()
            return Response(serializers.data)
        else:
            return Response(serializers.errors)

解释: 首先,我们定义了两个变量,queryset,serializer,一个是获取我们的数据,也就是Queryset对象,然后和序列化器,接着我们看到在get方法的时候,我们通过self.get_serializer(参数) 的形式来获取数据,post方法就不多说了,那我们来了看看他的实现原理 我们进入到GenericApiview

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):
        # Ensure queryset is re-evaluated on each request.
        queryset = queryset.all()
    return queryset

该函数是一个视图类中的方法,用于获取此视图的项目列表。它首先检查类是否包含一个非空的queryset属性,如果包含则返回该属性,否则会调用queryset属性的getter方法来获取queryset。如果queryset是一个QuerySet实例,则重新计算它,以确保在每次请求时都会重新计算。最终返回queryset。这个函数应该被重写而不是直接访问self.queryset,因为self.queryset只会被计算一次,并且结果将被缓存以供后续请求使用

def get_serializer(self, *args, **kwargs):
    """
    Return the serializer instance that should be used for validating and
    deserializing input, and for serializing output.
    """
    serializer_class = self.get_serializer_class()
    kwargs.setdefault('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)
    """
    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

该函数返回一个序列化类,作为序列化操作的基准。如果在当前类中定义了serializer_class属性,则返回该属性值;否则,返回None。如果未定义serializer_class属性且未重写get_serializer_class()方法,则抛出异常。

我们要注意,一个是获取示例对象,一个是获取序列化器这个类,两者是不一样的 UserinfoSerializers(instance=self.get_queryset(), many=True) self.get_serializer(instance=self.get_queryset(), many=True)
self.get_serializerc_class()(instance=self.get_queryset(), many=True)
上面三者是一致的 代码写到这里,那么我们可以发现,只要我们把一开始的queryset变量换一下并且serializer_class这个变量换一下,那么代码的其他地方就不用改了,帮助我们增加开发的效率

接着我们看另外的关于单个查询的代码

class BookDetailView(GenericAPIView):
    queryset = UserInfo.objects.all()
    serializer_class = UserinfoSerializers
    def get(self,request,pk):
        # 序列化传参是instance,反序列化是data
        serializer=self.get_serializer(instance=self.get_object(),many=False)
        return Response(serializer.data)
    def put(self,request,pk):

        serializers=self.get_serializer(instance=self.get_object(),data=request.data)
        if serializers.is_valid():
           #  更新逻辑
           # UserInfo.objects.filter(id=id).update(**serializers.validated_data)
           serializers.save()
           return Response(serializers.validated_data)
        else:
            return Response(serializers.errors)
    def delete(self,request,pk):
        self.get_object().delete()
        return Response("删除成功")

我们这里的第6行用到了self.get_object这个函数,那这个作用是什么呢

def get_object(self):
   
    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

该函数是一个视图类中的方法,用于返回所显示的对象。首先使用filter_queryset方法获取查询集,然后根据URL中的关键字参数进行查找过滤。最后,检查权限并返回对象

我们之前用self.get_queryset他调用的是queryset.all()也就是查询了所有,但是我们这里的单个查询,可以是id=???某个值,我们这相当于就是过滤条件,但是这里他是一个有名传参,我们需要在url中设置变量名,而且传入的时候也要是pk,那么就可以查询到数据了,当然要改也是可以的,因为源码中用到pk是因为lookup_field这个参数是pk,而后面的代码依这两个参数进行查询,所以我们可以改变这个值,来改变我们希望的变量名

image.png image.png