5-1 视图-DRF类视图

目录:

  • 类视图介绍
  • mixions封装常用操作
  • generics基于类的视图

一、类视图介绍

 使用类视图最好的好处就是可以创建复用的行为。我们在上一节 4-1 请求和响应-DRF请求和响应  中已经使用了简单的 类视图:APIView。但是我们发现 我们使用 APIView使用的时候,还是不能简化我们的代码,因为它是最基础的 视图。所有的都是继承它的。所以我们今天继续学习:

1、mixins:

mixins.ListModelMixin(获取所有数据),mixins.CreateModelMixin(创建一条数据)

mixins.RetrieveModelMixin(获取一条数据),mixins.UpdateModelMixin(全量更新和部分更新一条数据),mixins.DestroyModelMixin(删除一条数据)

 2、generics:

generics.GenericAPIView(继承APIView,提供queryset = None、serializer_class = None)

generics.ListCreateAPIView(继承mixins.ListModelMixin、mixins.CreateModelMixin、generics.GenericAPIView)

generics.RetrieveUpdateDestroyAPIView(继承mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,generics.GenericAPIView)

他们的继承关系如图:

二、mixions封装常用操作

  我们常用的操作比如创建、更新、删除、查找。REST框架已经帮我们写好了,写的代码就在mixins这个类里面。所以只有我们自己的视图直接继承这个类,就完全可以拥有上面的所有功能。

2.1、视图

说明:我们使用mixions封装了我们的增删改查的操作,大大降低了我们的代码量。一会我们看看里面到底封装了什么。

from .serializers import UserSerializer
from .models import User
from rest_framework import mixins  #提供数据增、删、改、查功能
from rest_framework import generics  #提供 queryset和serializer_class

#mixins.ListModelMixin(获取所有数据),mixins.CreateModelMixin(创建一条数据),generics.GenericAPIView(继承APIView,提供queryset = None、serializer_class = None)
class UserList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    queryset = User.objects.all()    #需要初始化generics.GenericAPIView 中的 queryset 和 serializer_class
    serializer_class = UserSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)  #直接调ListModelMixin中封装好的list()方法

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)  #直接调CreateModelMixin中封装好的 create()方法

#mixins.RetrieveModelMixin(获取一条数据),mixins.UpdateModelMixin(全量更新和部分更新一条数据),mixins.DestroyModelMixin(删除一条数据),同样要继承generics.GenericAPIView
class UserDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):

    queryset = User.objects.all()   #需要初始化generics.GenericAPIView 中的 queryset 和 serializer_class
    serializer_class = UserSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)  #直接调RetrieveModelMixin中封装好的retrieve()方法

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)  #直接调UpdateModelMixin 中封装好的 update()方法

    def patch(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)  #直接调UpdateModelMixin 中封装好的 update()方法

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)   #直接调 DestroyModelMixin 中封装好的 destroy()

2.2、路由配置

说明:因为采用CBV的方式,所以要改下 UserList的路由方式。注意:路由关键字的参数要是pk

from django.urls import path
from app03 import views


urlpatterns = [
    path("user/", views.UserList.as_view(), name="user-list"),  #采用CBV的路由方式
    path("user/<int:pk>/", views.UserDetail.as_view(), name="user-detail")
]

2.3、分析源码

我们随便找一个 mixinos中的ListModelMixin 来分析一下吧,看源码相信大家都会吧 =>  Ctrl + 选择 继承的类或者函数。好。那我们就来看一下ListModelMixin 的源码:

class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):  #封装好的list
        queryset = self.filter_queryset(self.get_queryset())  #需要 get_queryset

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)  #需要get_serializer
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)   #响应serializer.data
ListModelMixin源码

发现了啥,发现了,我们需要的UserList已经帮你封装好了,跟我们写的代码逻辑一模一样。我们来看看这个源码,里面需要 queryset  和 serializer。所以我们需要继承 generics.GenericAPIView,我们再看看generics.GenericAPIView的源码:

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    # You'll need to either set these attributes,
    # or override `get_queryset()`/`get_serializer_class()`.
    # If you are overriding a view method, it is important that you call
    # `get_queryset()` instead of accessing the `queryset` property directly,
    # as `queryset` will get evaluated only once, and those results are cached
    # for all subsequent requests.
    queryset = None       #提供了queryset 和 serializer_class 
    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_queryset
        """
        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

    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

    def get_serializer(self, *args, **kwargs):#提供 get_serializer
        """
        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
        }

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        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):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)
generics.GenericAPIView源码

从源码得知  generics.GenericAPIView 给我们提供了 ListModelMixin 所需要的 query_set 和 serializer_class 以及 get_queryset() 和 get_serializer()方法。

2.4、postman测试结果

1、页面访问:

 

2.postman测试结果:

1.GET:http://127.0.0.1:8000/api/user/

出参:
[
    {
        "id": 2,
        "phone": "13641929925",
        "name": "gaogao",
        "gender": 1
    },
    {
        "id": 3,
        "phone": "13641929927",
        "name": "gaogao",
        "gender": 2
    }
]

2.POST:http://127.0.0.1:8000/api/user/

入参:
{
    "phone": "13641929934",
    "name": "傻逼鸿",
    "gender": 2,
    "pwd": "1234",
    "pwd1": "1234"
}

出参:
{
    "id": 7,
    "phone": "13641929934",
    "name": "傻逼鸿",
    "gender": 2
}

3、PUT: http://127.0.0.1:8000/api/user/7/

入参:
{
    "phone": "13641929944", #修改数据
    "name": "鸿哥哥",  #修改数据
    "gender": 2,
    "pwd": "1234",
    "pwd1": "1234"
}

出参:
{
    "id": 7,
    "phone": "13641929944",
    "name": "鸿哥哥",
    "gender": 2
}


4、PATCH: http://127.0.0.1:8000/api/user/7/
入参:
{
    "phone": "13641929954",
    "name": "鸿哥哥",
    "gender": 1
}

出参:
{
    "id": 7,
    "phone": "13641929954",
    "name": "鸿哥哥",
    "gender": 1
}

5、DELET: http://127.0.0.1:8000/api/user/7/
postman测试结果

三、generics基于类的视图

  我们可以看到我们的代码量已经减少了很多,但是我们还可以继续优化。REST把我们常见的操作继续封装了起来,封装起来的代码就在我们的 ListCreateAPIView 和 RetrieveUpdateDestroyAPIView。

这样我们的代码量可以继续减少。

3.1、视图

说明:既然它说的这么神奇,我们来看看,到底有多牛。

from .serializers import UserSerializer
from .models import User
from rest_framework import generics


class UserList(generics.ListCreateAPIView):  #继承ListCreateAPIView
    
    queryset = User.objects.all()    #只提供序列化类(serializer_class)和元数据(query_set)
    serializer_class = UserSerializer

class UserDetail(generics.RetrieveUpdateDestroyAPIView):  #继承RetrieveUpdateDestroyAPIView

    queryset = User.objects.all()
    serializer_class = UserSerializer

哈哈,果然更加简单的减少我们的代码。

3.2、ListCreateAPIView 和 RetrieveUpdateDestroyAPIView源码 剖析

1、ListCreateAPIView源码

说明:不看不知道,一看吓一跳,原来 ListCreateAPIView(mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView) 继承了ListModelMixin,CreateModelMixin,GenericAPIView。哈哈确实帮我们封装的好好的。

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

2.RetrieveUpdateDestroyAPIView 源码

说明:RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,GenericAPIView)。跟上面一样都是继承RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView。

class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
    """
    Concrete view for retrieving, updating or deleting a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
RetrieveUpdateDestroyAPIView源码

我就不把测试结果放在这边拉。

posted @ 2020-04-27 10:55  帅丶高高  阅读(492)  评论(0)    收藏  举报