DRF的视图

  视图函数基本上都是处理增删改查的操作,对应4个方法:get、post、put、delete。

视图的基本处理方法

  写DRF我们都会使用CBV模式去处理视图逻辑。

  一般的方法就是根据不同的请求定义相应的方法去具体处理业务逻辑,详见下面这两篇博客:

       基于DRF的图书增删改查练习

       DRF的序列化器serializers

继承DRF自带的混合类的写法

  DRF为我们提供了不同的“混合类”,我们的视图类继承这些混合类,然后指定对应的Model的对象及序列化器的类就可以快速实现增删改查的操作了。

  还拿DRF的序列化器serializers这里的例子来说,我们用DRF自带的混合类实现一下出版社的增删改查。

2个路由2个视图的写法

  注意路由的写法:

re_path(r'^publishers/$',views.PublishList.as_view()),
# 使用组合类的话这里必须加上有名分组!pk是查询的条件!
re_path(r'^publishers/(?P<pk>\d+)/$',views.PublishDetail.as_view()),

  视图的写法如下:

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView

# 继承相应的混合类
class PublishList(ListCreateAPIView):
    # 指定Model中的对象
    queryset = models.Publisher.objects.all()
    # 指定序列化的类
    serializer_class = PubSerializers


class PublishDetail(RetrieveUpdateDestroyAPIView):

    queryset = models.Publisher.objects.all()
    serializer_class = PubSerializers

2个路由1个视图的写法——viewsets.ModelViewSet

  路由的写法:

re_path(r'^publishers/$',views.PublishView.as_view(actions={"get":"list","post":"create"})),
# 使用组合类的话这里必须加上有名分组!默认pk是查询的条件!
re_path(r'^publishers/(?P<pk>\d+)/$',views.PublishView.as_view(actions={"get":"retrieve","put":"update",'patch':'partial_update',"delete":"destroy"})),

  视图函数中,as_view方法里面的actions这个参数十分重要!

  路由分发的源码浅析:

  通过看源码可以知道:

  执行自定义类PublishView的as_view方法的时候,从它继承的父类ModelViewSet中找~这个父类ModelViewSet又继承了一个GenericViewSet类,这个类又继承了ViewSetMixin类~~ViewSetMixin这个类中重写了as_view方法!

  ViewSetMixin类中的as_view方法接收了参数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

  设置成的值“handler”,对应的就是actions这个字典请求方法为key后面的value,这些value对应的方法都写在了“混合类”中!  

  视图的写法:

from rest_framework.viewsets import ModelViewSet,GenericViewSet
from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin
class PublishView(ModelViewSet):
    queryset = models.Publisher.objects.all()
    serializer_class = PubSerializers

  混合类一览

  我们可以看一下ModelViewset类,其实它继承了很多的混合类:

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

  这些混合类其实就在rest_framework.mixins中:

"""
Basic building blocks for generic class based views.

We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings


class CreateModelMixin:
    """
    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 {}


class ListModelMixin:
    """
    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)


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


class UpdateModelMixin:
    """
    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)

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

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)


class DestroyModelMixin:
    """
    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()

  其实,每一个混合类对应的就是“增删改查查(查询所有数据与查询单个数据)”的操作——以后我们在写视图的时候,如果需求是:这个视图只有某些功能(比如只能查询),那么 我们的视图类只要继承其中的某几个组合类就好了—— 十分方便!

 

posted on 2019-08-15 15:38  江湖乄夜雨  阅读(242)  评论(0编辑  收藏  举报