视图组件

视图组件

基本视图

路由

    url(r'^publish/$', views.PublishView.as_view()),
    url(r'^publish/(?P<pk>d+)/',views.PublishDetailView.as_view()),

serializer.py

from app01 import models
class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Publish
        fields='__all__'

view.py

class PublishView(APIView):

    def get(self, request):
        publish_list = models.Publish.objects.all()
        bs = PublishSerializers(publish_list, many=True)
        # 序列化数据

        return Response(bs.data)

    def post(self, request):
        # 添加一条数据
        print(request.data)

        bs=PublishSerializers(data=request.data)
        if bs.is_valid():
            bs.save()  # 生成记录
            return Response(bs.data)
        else:

            return Response(bs.errors)

class PublishDetailView(APIView):
    def get(self,request,pk):
        publish_obj=models.Publish.objects.filter(pk=pk).first()
        bs=PublishSerializers(publish_obj,many=False)
        return Response(bs.data)
    def put(self,request,pk):
        publish_obj = models.Publish.objects.filter(pk=pk).first()

        bs=PublishSerializers(data=request.data,instance=publish_obj)
        if bs.is_valid():
            bs.save() # update
            return Response(bs.data)
        else:
            return Response(bs.errors)
    def delete(self,request,pk):
        models.Publish.objects.filter(pk=pk).delete()

        return Response("")

mixin类和generice类编写视图

ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin

五个类中封装了查所有、新创 | 查一个、更新、删除 五种方法

但是在这五个方法中用到的get_serializer、get_object等方法均在GenericAPIView类中

而GenericAPIView类需要queryset或者serializer_class参数

因此我们只需要让视图类继承上述五个类和GenericAPIView类组合即可实现简单的增删改查的接口

views.py

from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin
from rest_framework.generics import GenericAPIView
class PublishView(ListModelMixin,CreateModelMixin,GenericAPIView):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)

class PublishDetailView(RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers
    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 delete(self,request,*args,**kwargs):
        return self.destroy(request,*args,**kwargs)

 这个时候,我们只需要提供queryset和serializer_class两个参数配置,mixin包下面的类会帮我们处理数据,我们调用对应的方法并且将其返回值返回即可,

但是需要注意的是,如果使用此方法,urls.py的url对应的id要命名为pk,如下:

url(r'^publishes/$', views.PublishView.as_view()),
url(r'^publishes/(?P<pk>d+)/$', views.PublishDetailView.as_view()),

但是,即使我们用了这种封装,很多代码还是有重复的,所有,rest_framework又给我们做了一层封装

使用generics 下ListCreateAPIView,RetrieveUpdateDestroyAPIView

ListCreateAPIView类

继承了ListModelMixin,CreateModelMixin和GenericAPIView

在类体代码中实现了get 和 post 方法 并返回了序列化结果

RetrieveUpdateDestroyAPIView类

继承了RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin和GenericAPIView

在类体代码中实现了get 、 (put、patch)、delete方法 并返回了序列化结果

所以有如下写法

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
class PublishView(ListCreateAPIView):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

class PublishDetailView(RetrieveUpdateDestroyAPIView):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

这样,代码就清晰很多了,但是,这种方法依然是将一个model表分成两个视图,那,有没有一种方法能将他们合并在一起呢?

使用ModelViewSet

路由:

    url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
    url(r'^publish/(?P<pk>d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),

views.py

from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

自定义方法与不同请求方式对应

url.py

    url(r'^aa/$', views.PublishView.as_view({'get': 'aaa'})),
    url(r'^bb/$', views.PublishView.as_view({'get': 'bbb'})),

views.py

from rest_framework.viewsets import  ViewSetMixin
from rest_framework.views import  APIView
# ViewSetMixin 重写了as_view方法
class Test(ViewSetMixin,APIView):

    def aaa(self,request):
        return Response()
	def bbb(self,request):
		return Response()

源码分析

普通的CBV中的视图类as_view()是无法传参的,ViewSetMixin 重写了as_view方法

rest_framework>viewsets>ViewSetMixin

部分源码如下:

@classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        """
        Because of the way class based views create a closure around the
        instantiated view, we need to totally reimplement `.as_view`,
        and slightly modify the view function that is created and returned.
        """
       ......
    # 若继承了ModelViewSet类必须在路由中传入actions
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")

     ......
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            # We also store the mapping of request methods to actions,
            # so that we can later set the action attribute.
            # eg. `self.action = 'list'` on an incoming GET request.
            self.action_map = actions

            # Bind methods to actions
            # This is the bit that's different to a standard view
            # 将视图类中的action方法与请求方式绑定 
            # 不同的请求方式对应出发不同的方法
            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

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # And continue as usual
            return self.dispatch(request, *args, **kwargs)

主键中各个类的继承关系

各个类关系分析

首先我们得了解一下,当我们不去自己继承View类定义类视图时,DRF给哦们提供了几个继承自View类的丰富的类

基类:APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

APIViewView的不同之处在于:

传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
任何APIException异常都会被捕获到,并且处理成合适的响应信息;
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性:

authentication_classes 列表或元祖,身份认证类
permissoin_classes 列表或元祖,权限检查类

throttle_classes 列表或元祖,流量控制类

APIView中仍和普通的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。

2.基类:GenericAPIView

提供的关于序列化器使用的属性与方法

GenericAPIView
继承自APⅣew,主要增加了操作序列化器和数
据库查询的方法,作用是为下面Mixn扩展类的
执行提供方法支持。通常在使用时,可搭配一个
或多个Min扩展类
指明视图使用的序列化器
1通过属性: serializer_ class
2通过方法: get serializer_class(self
get serializer(self, args, * kwargs)
数据库查询的属性与方法
指明使用的数据查询集
1通过属性: queryset
2通过方法: get queryset(se)
get object(self)

配合GenericAPIView使用的几个扩展类

我们发现前边的两个父类还需要进行函数的定义去处理不同的请求,这样我们还得在函数里边实现查询集以及序列化器的指定,工作量仍然很大。怎么解决呢?

GenericAPIView的五个扩展类给我们提供了五个方法分别进行增删改查的不同操作,这样我们就不用写那么多函数啦!!

搭配GenericAPIView使用

1.ListModelMixin: 提供list方法快速实现列表视图

2.CreateModelMixin: 提供create方法快速实现创建资源的视图

3.RetrieveModelMixin 提供retrieve方法,可以快速实现返回一个存在的数据对象(需要传入pk)

4.UpdateModelMixin 提供update方法,可以快速实现更新一个存在的数据对象。 提供partial_update方法,可以实现局部更新

5.DestroyModelMixin 提供destroy方法,可以快速实现删除一个存在的数据对象

1.CreateAPIView(等价于GenericAPIView+CreateModelMixin) 提供 post 方法 继承自: GenericAPIView、CreateModelMixin

2.ListAPIView 提供 get 方法 继承自:GenericAPIView、ListModelMixin

3.RetrieveAPIView 提供 get 方法 继承自: GenericAPIView、RetrieveModelMixin

4.DestoryAPIView 提供 delete 方法 继承自:GenericAPIView、DestoryModelMixin

5.UpdateAPIView 提供 put 和 patch 方法 继承自:GenericAPIView、UpdateModelMixin

6.RetrieveUpdateAPIView 提供 get、put、patch方法 继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7.RetrieveUpdateDestoryAPIView 提供 get、put、patch、delete方法 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

所以代码中的mixins.RetrieveModelMixin, GenericAPIView可以写成RetrieveAPIView

原文地址:https://www.cnblogs.com/9527mwz/p/11200592.html