rest_framework 之视图三部曲

一、视图三部曲

1  使用混合(mixins)

上一节的视图部分:

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers


from rest_framework import serializers


class BookSerializers(serializers.ModelSerializer):
      class Meta:
          model=Book
          fields="__all__"
          #depth=1


class PublshSerializers(serializers.ModelSerializer):

      class Meta:
          model=Publish
          fields="__all__"
          depth=1


class BookViewSet(APIView):

    def get(self,request,*args,**kwargs):
        book_list=Book.objects.all()
        bs=BookSerializers(book_list,many=True,context={'request': request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):
        print(request.data)
        bs=BookSerializers(data=request.data,many=False)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class BookDetailViewSet(APIView):

    def get(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,context={'request': request})
        return Response(bs.data)

    def put(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,data=request.data,context={'request': request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishViewSet(APIView):

    def get(self,request,*args,**kwargs):
        publish_list=Publish.objects.all()
        bs=PublshSerializers(publish_list,many=True,context={'request': request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):

        bs=PublshSerializers(data=request.data,many=False)
        if bs.is_valid():
            # print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishDetailViewSet(APIView):

    def get(self,request,pk):

        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,context={'request': request})
        return Response(bs.data)

    def put(self,request,pk):
        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

 mixin类编写视图 :封装逻辑

urls.py

 url(r'^books/$', views.BookView.as_view(),name="books"),#
 url(r'^books/(\d+)/$', views.BookDetailView.as_view(),name="detailbook"),#

(1)查看 

from rest_framework import mixins
from rest_framework import generics
#
class BookViewSet(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView): #GenericAPIView继承了APIView,APIView继承了View,用户访问时相当于执行View方法->执行dispatch方法
->执行get方法->相当于执行list方法 queryset = Book.objects.all() #不能改这两个变量的名字 serializer_class = BookSerializers# def get(self, request, *args, **kwargs): #整个获取数据的逻辑被封装到ListModelMixin这个类下的List方法中,                             以后再有关于所有获取数据的逻辑都从这个类下取 return self.list(request, *args, **kwargs) #list->get->dispatch->View# 
查看mixins.ListModelMixin源码
class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset()) #get_queryset方法封装在GenericAPView类中,拿到了queryset数据,
            这里的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)
查看generics.GenericAPIView源码
#
class GenericAPIView(views.APIView):#

    queryset = None
    serializer_class = None

    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):   #

        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  #从自己当前类(上面是BookViewSet)找queryset,拿到数据交给queryset
        if isinstance(queryset, QuerySet):  #
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()  #
        return queryset

(2)添加

class BookViewSet(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):

    queryset = Book.objects.all()
    serializer_class = BookSerializersdef post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
 查看mixins.CreateModelMixin源码
#
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) #相当于save操作
        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 {}

(3)单条数据的get请求

class BookDetailViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Book.objects.all()  #
    serializer_class = BookSerializers#
#
    def get(self, request, *args, **kwargs):#
        return self.retrieve(request, *args, **kwargs)#

查看mixins.RetrieveModelMixin源码

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

(4)单条数据的delete请求

class BookDetailViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

    def delete(self, request, *args, **kwargs): #
        return self.destroy(request, *args, **kwargs)#

查看mixins.DestroyModelMixin源码

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()#

(4)单条数据的put请求

class BookDetailViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

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

查看mixins.UpdateModelMixin源码

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)#save操作,本质上是一个更新操作

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

 以上这种方式代码量和之前相比没少多少,但主要逻辑都帮我们封装起来了

 2 使用通用的基于类的视图

 通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。 

from rest_framework import generics

class BookView(generics.ListCreateAPIView): #
    queryset=Author.objects.all()#
    serializer_class =AuthorModelSerializers#
    
    
#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)#

单条数据的查看、更新和删除

from rest_framework import generics

class BookDetailView(generics.RetrieveUpdateDestroyAPIView):#
    queryset = Author.objects.all()#
    serializer_class = AuthorModelSerializers#
#generics.py 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):#put和patch都是更新 return self.partial_update(request, *args, **kwargs)# def delete(self, request, *args, **kwargs):# return self.destroy(request, *args, **kwargs)#

3 viewsets.ModelViewSet:

统一了上面两个视图类,主要在url这里做了文章

之前的as_view没有传参数,用户访问时找到的as_view是APIView(View)类下面没有处理参数的as_view方法

现在我们把url进行以下设置:

urls.py

   #en
   url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"),
    url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({
                'get': 'retrieve',#
                'put': 'update',#
                'patch': 'partial_update',#
                'delete': 'destroy'#
            }),name="book_detail"),#

上面的url中的as_view传了参数:通过一个字典,传入参数,指定什么请求方式用哪一个内部方法来执行

views.py

#
from rest_framework import viewsets

#只需一个类就能实现get、post、put、patch、delete请求
class BookViewSet(viewsets.ModelViewSet):#
    queryset = Book.objects.all()#
    serializer_class = BookSerializers#

当用户通过get/post请求访问了books之后: -> views.BookViewSet.as_view

现在我们来找as_view方法  BookViewSet->ModelViewSet->GenericViewSet->ViewSetMixin 

class ViewSetMixin:#         (1)
    """
    This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the 'GET' and 'POST' methods
    to the 'list' and 'create' actions...

    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):#actions就是我们传的那个字典 {"get":"list","post":"create"}       (2)

        def view(request, *args, **kwargs): #覆盖了View类下的view方法  (4)
            self = cls(**initkwargs)#
            self.action_map = actions #actions就是我们传的那个字典  {"get":"list","post":"create"}

            for method, action in actions.items(): #method是请求方式,action是实例方法
                handler = getattr(self, action)  #反射找实例方法  self.list   self.create
                setattr(self, method, handler)  #为请求方式设置对应的实例方法

                # setattr(self,"get",self.list)  #这两步相当于:self.get=self.list  self.list =self.create
                # setattr(self,"post",self.create)

            #……
            # And continue as usual
            return self.dispatch(request, *args, **kwargs)#  (5)
            #查找dispatch:->BookViewSet->ModelViewSet-> GenericViewSet->ViewSetMixin->
            # GenericAPIView(views.APIView)->APIView最后找到了APIView下面的dispatch方法
    #……     return csrf_exempt(view) #返回的是当前ViewSetMixins类下的view方法 (3)

#rest_framework\view.py class APIView(View):# #…… def dispatch(self, request, *args, **kwargs): (6) #…… try: self.initial(request, *args, **kwargs)# # Get the appropriate handler method if request.method.lower() in self.http_method_names:# handler = getattr(self, request.method.lower(), #handler就是list、create方法 self.http_method_not_allowed)# else: handler = self.http_method_not_allowed# response = handler(request, *args, **kwargs) #返回的就是list、create方法 (8) #如果get请求走的就是ListModelMixin类下的list方法 #如果post请求走的就是CreateModelMixin类的create方法 #最后执行上述方法后把返回值传给用户 #返回值执行过程:response->dispatch->ViewSetMixin类下的view-> views.BookViewSet.as_view return self.response# (7) 

 

上一篇:Python--函数传参


下一篇:Python中*args,**kwargs两个参数的作用?