jquery循环 request中的list_详解Django DRF框架中APIView、GenericAPIView、ViewSet区别

e22c56ba83381b1c5e37d36ceae54f5e.png

最近阅读了Django REST framework框架官方文档以及部分源码,感想如下

官方文档地址: https://www.django-rest-framework.org/

首先将两个概念,FBV开发模式与CBV开发模式

FBV指的时Function Base View,基于函数开发视图

CBV指的时Class Base View,基于对象开发视图

本文仅讨论CBV模式中,APIView、GenericAPIView、ViewSet的使用与区别,以及部分源代码实现,继承关系

  1. APIView

APIView与Django的View类似,我们的业务类只需要继承APIView,在URL传递过程,我们只需要调用APIView的as_view()方法,然后URL就会调用业务类对应的HTTP方法,如get,post,put,delete(对应查 增 改 删)方法,我们在业务代码中只需要实现三个功能即可实现get方法

  1. ORM调用
  2. 序列化
  3. 返回数据

示例代码如下:

# APIView_test.py
class SnippetList(APIView):

    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

2. GenericAPIView

而使用GenericAPIView可以使我们的代码更简洁

例如上述代码,我们可以简化为

# GenericAPIView_test.py
class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView,
                  ):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        # 我们不用在get post两个方法中都去ORM调用以及序列化调用
        return self.list(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(*args, **kwargs)

通过继承GenericAPIView,对类的queryset与serializer_class属性赋值,然后get方法可以调用父类的ListModelMixin方法的list()方法,而post方法可以调用父类CreateModelMixin的方法,其他URL分发等与之前的APIView类似

实际上,我们的GenericAPIView继承于APIView,而之前的get方法中ORM调用

# APIView_test.py
snippets = Snippet.objects.all()

调用ORM实际上会在GenericAPIView的get_queryset()方法被使用,

# rest_framework/generics.py
class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    """
    很多注释
    """
    queryset = None  # !!! 传入的queryset
    serializer_class = None

    # If you want to use object lookups other than pk, set 'lookup_field'.
    # For more complex lookup requirements override `get_object()`.
    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

    """
    省略的掉其他代码,让我们专注与get_query方法
    """

而get方法中序列化调用

# APIView_test.py
serializer = SnippetSerializer(snippets, many=True)

序列化对象则会在GenericAPIView的get_serializer()方法被使用

# rest_framework/generics.py
class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    queryset = None
    serializer_class = None  # !!!注意

    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['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

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }
    """
    让我们省略其他代码,专注于get_serializer的实现
    """

当我们调用

# APIView_test.py
def get(self, request, *args, **kwargs):
        return self.list(*args, **kwargs)

方法,实际上会调用其继承的ListModelMixin类,其中ListModelMixin类会调用

# rest_framework/mixins.py
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)

GenericAPIView的get_queryset()方法,将传入的ORM进行调用,

这样我们就不用每次getpostputdelete代码中再进行ORM调用与serializer序列化

当然,这样每次都继承三个类是比较麻烦的,我们可以用一个类继承这三个类,然后我们的业务实例只需要继承一个类即可完成这些工作,当然,Django REST framework已经帮我们做好了

我们可以通过一种混合视图的方法来进行继承

class SnippetList(generics.ListCreateAPIView):

    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

通过继承ListCreateAPIView类,而ListCreateAPIView类则为

# rest_framework/generics.py
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)

这不正是我们之前继承GenericAPIView写的类吗?

所以,通过混合视图的方法,可以简化我们的代码,使我们代码更加简洁.

3. ViewSets

viewsets与普通view实际上相同,但不需要使用get/put的方式提供更新与添加操作

通常使用Router实例化一组viewsets

与之前的代码不同,比如我们在定义Snippet view的时候定义了两个,一个位SnippetList,另一个为SnippetDetail,但我们可以通过viewsets定义一个类,实现之前两个类才能实现的方法。

完成后我们需要将viewsets绑定到url

在设置url的时候我们无需像以前那种方式自己在urlpartten中添加,而是采用router方式进行路由注册,减少代码

# viewset_test.py
class SnippetViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.

    Additionally we also provide an extra `highlight` action.
    """
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

而其中继承的ModelViewSet方法

# rest_framework/viewsets.py
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

同时继承了CreateModelMixin、RetrieveModelMixin、UpdateModelMixin与GenericViewSet等几个类,也就是说,继承ModelViewSet可以同时实现增删改查方法,而不用我们自己去写get、post、put、delet方法,减少代码

总结:

  1. viewset实际上属于继承多个mixin模块的Model,在代码最少的情况下让我们实现符合REST风格的增删改查接口
  2. GenericViewSet实际上继承与APIView,在我们不需要完全实现增删改查,而是只实现部分方法地时候,使用混合视图比较方便
  3. 普通APIView与Django View类似,通过CBV方式重载父类的get、post、put、delet方法,实现REST风格的接口
  4. 当然FBV方式也不是不可取,如我们需要实现一个简单的接口,如获取服务器时间,通过FBV方法其实是最快的一种方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值