DRF从入门到精通四(视图基类、GenericAPIView的视图扩展类、视图子类、视图集父类、子类)

前言

Django REST framwork 提供的视图的主要作用:

  • 控制序列化器的执行(检验、保存、转换数据)
  • 控制数据库查询的执行

虽说视图也可以基于FBV模式来进行开发,但是使用CBV能够让我们避免很多重复代码,类具备继承、封装、多态等等,而DRF也给我们提供了非常丰富的视图组件

一、视图基类

DRF提供了众多通用的视图基类与拓展类,以供我们简化视图的编写。

APIView基类

APIView是DRF提供的所有视图的基类(父类),而APIView则继承自Django的View作为父类
APIViewView不同之处在于:

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

支持定义的类属性(部分):

属性名定义方式作用
authentication_classes列表或元祖权限检查类
permissoin_classes列表或元祖列表或元祖
throttle_classes列表或元祖流量控制类

APIView中仍以常规的类视图定义方法来实现get() post() 或者其它方法来处理请求;
例如:

	from rest_framework.views import APIView
	from rest_framework.response import Response
	from .serializer import BookSerializer
	from . import models
	
	class BookView(APIView):
    def get(self, request):
        book = models.Book.objects.all()
        ser = BookSerializer(instance=book, many=True)
        return Response(ser.data)

这也就是我们平常所编写的,获取数据、传入序列化器、得到序列化后的内容


GenericAPIView通用视图基类

	from rest_framework.generics import GenericAPIView
	class BookView(GenericAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

继承自APIView,主要增加了操作序列化器和数据库查询的方法,作用是为了Mixin扩展类的执行提供方法支持,通常在使用时,可搭配一个或多个Mixin扩展类。

需要使用的类属性:

  • queryset:指明使用的数据查询集
  • serializer_class:指明视图使用的序列化器

内部常用的方法:
get_serializer_class(self)

  • 当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了
  • 返回序列化器类,默认返回serializer_class

一般不会调用
get_queryset():获取查询集(等同于获取queryset类属性的值)

	class BookView(GenericAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
		
		'''提供了拓展性,可以定制返回内容'''
	    def get_serializer_class(self):
	        if self.request.method == 'GET':
	            return models.Book.objects.all()
	        else:
	            return models.Publish.objects.all()
		
	    def get(self, request):
	        obj_list = self.get_queryset()

get_serializer(self,*args,**kwargs)

返回序列化器对象,主要用来提供给Mixin拓展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。

注意:该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、viwe,这三个数据对象可以在定义序列化器时使用。

  • request 当前视图的请求对象
  • view 当前请求的类视图对象
  • format 当前请求期望返回的数据格式
	class BookView(GenericAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
	    
	    def get(self, request):
	        obj_list = self.get_queryset()
	        ser = self.get_serializer(instance=obj_list, many=True)  # 同正常调用序列化器一样
	        return Response(ser.data)

get_object():返回详情视图所需的模型类数据对象(方法必须要有一个形参名为pk),若详情访问的模型类对象不存在,会返回404

	class BookDetailView(GenericAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

	    def get(self, request, *args, **kwargs):  # kwargs内必须有一个key名为pk
	        obj = self.get_object() # 获取某个对象,它的主键值与kwargs里的pk值相等
	        ser = self.get_serializer(instance=obj) # 传入序列化器
	        return Response(ser.data)

其他可以设置的属性

  • pagination_class 指明分页控制类
  • filter_backends 指明过滤控制后端

整体代码展示

	'''继承GenericAPIView+序列化类+Response'''
	from rest_framework.generics import GenericAPIView
	from rest_framework.response import Response
	class BookView(GenericAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
	    def get(self, request):
	        obj_list = self.get_queryset()
	        ser = self.get_serializer(instance=obj_list, many=True)
	        return Response(ser.data)
	
	    def post(self, request):
	        ser = self.get_serializer(data=request.data)
	        if ser.is_valid():
	            ser.save()
	            return Response(ser.data)
	
	        else:
	            return Response(ser.errors)

	class BookDetailView(GenericAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
	
	    '''提供了拓展性,可以定制返回内容'''
	    def get_serializer_class(self):
	        if self.request.method == 'GET':
	            return models.Book.objects.all()
	        else:
	            return models.Publish.objects.all()
	
	     def get(self, request, *args, **kwargs):  # kwargs内必须有一个key名为pk
	        obj = self.get_object() # 获取某个对象,它的主键值与kwargs里的pk值相等
	        ser = self.get_serializer(instance=obj) # 传入序列化器
	        return Response(ser.data)
	
	    def put(self, request, *args, **kwargs):
	         obj = self.get_object()
	        if obj:
	            ser = self.get_serializer(instance=obj,data=request.data)
	            if ser.is_valid():
	                ser.save()
	                return Response(ser.data)
	            else:
	                return Response(ser.errors)
	        else:
	            return Response('修改的数据不存在')

	
	    def delete(self, request, *args, **kwargs):
	        self.get_object().delete()
	        return Response('')

序列化类

	class BookSerializer(serializers.ModelSerializer):
	    class Meta:
	        model = models.Book
	        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']
	        extra_kwargs = {
	            'publish': {'write_only': True},
	            'authors': {'write_only': True},
	            'publish_detail': {'read_only': True},
	            'author_list': {'read_only': True},
	        }

可能暂时没有看出GenericAPIView这个类带给我们的优势,因为我们这个GenericAPIView是作为基类使用的,需要搭配Mixin拓展类一起使用。


二、GenericAPIView的视图拓展类

提供了几种后端视图(对数据资源机械能增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来服用代码,减少自己编写的代码量。

这五个拓展类需要搭配GenericAPIView父类,因为五个拓展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。

在实例开始前,准备序列化器:

	from . import models
	from rest_framework import serializers
	class BookSerializer(serializers.ModelSerializer):
	    class Meta:
	        model = models.Book
	        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']
	        extra_kwargs = {
	            'publish': {'write_only': True},
	            'authors': {'write_only': True},
	            'publish_detail': {'read_only': True},
	            'author_list': {'read_only': True},
	        }

路由配置

	我这里使用了路由分发,include
	'总路由'
	from django.contrib import admin
	from django.urls import path, include
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('api/v1/', include('app01.urls')),
	]
	
	'子路由app01/urls.py'
	from django.urls import path
	from . import views
	urlpatterns = [
	    path('books/',views.BookView.as_view()),
	    path('books/<int:pk>',views.BookDetailView.as_view()),
	]

1.ListModelMixin

列表视图拓展类,提供list(reqeust,*args,**kwargs)方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页。

源码

	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)

举例说明:
未调用list方法前:

	from rest_framework.generics import GenericAPIView
	class BookView(GenericAPIView):
	    queryset = models.Book.objects.all()  # 指定序列化的数据(GenericAPIView内部会调用该方法)
	    serializer_class = BookSerializer #指定序列化器
	    def get(self, request):
	        obj_list = self.get_queryset()  # 获取需要序列化的数据(等同于获取了queryset的值)
	        ser = self.get_serializer(instance=obj_list, many=True) #将序列化数据传入序列化器,得到序列化器的对象
	        
	        return Response(ser.data)

调用list方法后:

	from rest_framework.generics import GenericAPIView
	from rest_framework.mixins improt ListModelMixin
	class BookView(GenericAPIView,ListModelMixin):
	    queryset = models.Book.objects.all()  # 指定序列化的数据(GenericAPIView内部会调用该方法)
	    serializer_class = BookSerializer #指定序列化器
	
	    def get(self, request):
	    	'内部list方法会将需要序列化的数据传给序列化器,然后返回序列化器独享data数据'
	        return super().list(request)

这两种方式实现的效果相同,但是明显下面代码会少一些。

在这里插入图片描述


2.CreateModelMixin

创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。

源码

	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)
	         # 调用 perform_create 方法执行实际的创建操作
	         '调用下面的方法,将序列化器对象传入'
	        self.perform_create(serializer)
	        # 获取成功创建后的响应头信息
	        headers = self.get_success_headers(serializer.data)
	        # 返回包含新创建实例数据的响应,HTTP 状态码为 201 Created
	        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
	
	    def perform_create(self, serializer):
	    	# 调用序列化器的 save 方法,将数据保存到数据库中
	    	'由于我们序列化器继承的是ModelSerializer,所以不需要重写create方法就能将数据新增'
	        serializer.save()
	
	    def get_success_headers(self, data):
	    # 获取成功创建后的响应头信息,通常是新创建实例的位置 URL
	        try:
	            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
	        except (TypeError, KeyError):
	            return {}

使用方式:

	from rest_framework.mixins import CreateModelMixin,ListModelMixin
	class BookView(GenericAPIView,CreateModelMixin,ListModelMixin):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
	
	    def get(self, request):
	        return super().list(request)
	
	    def post(self, request):
	        return super().create(request)

在这里插入图片描述

此时我们的代码已经开始越写越少了,因为继承的类已经帮助我们调用了正常需要调用的代码,此后新增直接调用即可,但也不排除少数情况需要我们重写create方法。


3.RetrieveModelMixin

详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。

源码

	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)

本质就是在kwargs获取一个key为pk的关键字的值,然后在queryset查询集内获取pk值相同的对象传入序列化器,响应序列化后的数据。

使用方式:

	from rest_framework.mixins import RetrieveModelMixin
	class BookDetailView(GenericAPIView, RetrieveModelMixin):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
	    
	    def get(self, request, *args, **kwargs):
	    'retrieve方法本质上还是调用了self.get_object方法拿到对象传给序列化器,然后返回序列化器的data数据'
	       return super().retrieve(request,*args,**kwargs)

在这里插入图片描述


4.UpdateModelMixin

更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

成功返回200,序列化器校验数据失败时,返回400错误。

源码

	class UpdateModelMixin:
	    """
	    Update a model instance.
	    """
	    def update(self, request, *args, **kwargs):
	    	 # 获取是否为部分更新的标志(partial)
	        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)
	        
	        # 调用 perform_update 方法执行实际的更新操作
	        self.perform_update(serializer)
			
			# 如果已经应用了 'prefetch_related' 到查询集,需要强制使模型实例的预取缓存无效
	        if getattr(instance, '_prefetched_objects_cache', None):
	            instance._prefetched_objects_cache = {}
	            
			 #返回包含更新后序列化数据的响应
	        return Response(serializer.data)
		
	    def perform_update(self, serializer):
	    # 调用序列化器的 save 方法,将更新后的数据保存到数据库中
	    '序列化器接收了instance对象,执行save后会执行内部的update方法'
	        serializer.save()
		
	    def partial_update(self, request, *args, **kwargs):
	     # 部分更新的快捷方法,设置 'partial' 标志为 True
	        kwargs['partial'] = True
	        return self.update(request, *args, **kwargs)

使用方式:

	from rest_framework.mixins import UpdateModelMixin
	class BookDetailView(GenericAPIView, UpdateModelMixin):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer
	
	    def put(self, request, *args, **kwargs):
	    '内部执行原理:先获取某个对象,然后一并将request.data数据传给序列化器,拿到序列化器对象的data数据'
	        return super().update(request, *args, **kwargs) #UpdateModelMixin类的内部的方法

在这里插入图片描述


5.DestroyModelMixin

删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。

源码

	class DestroyModelMixin:
	    """
	    Destroy a model instance.
	    """
	    def destroy(self, request, *args, **kwargs):
	    	# 获取要删除的模型实例
	    	'获取需要删除的对象'
	        instance = self.get_object()
	         # 调用 perform_destroy 方法执行实际的删除操作
	         '执行删除操作'
	        self.perform_destroy(instance)
	         # 返回空内容的响应,HTTP 状态码为 204 No Content,表示成功删除
	        return Response(status=status.HTTP_204_NO_CONTENT)
	
	    def perform_destroy(self, instance):
	   		# 调用模型实例的 delete 方法,从数据库中删除该实例
	        instance.delete()

使用方式:

	from rest_framework.mixins import DestroyModelMixin
	class BookDetailView(GenericAPIView,DestroyModelMixin):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

	    def delete(self, request, *args, **kwargs):
	        return super().destroy(request, *args, **kwargs) # DestroyModelMixin类内部方法

在这里插入图片描述

此时我们整体代码相对平常编写的视图已经减少了很多,并且能够正常实现功能。下面了解到的将会使我们的代码更加简洁、并保证了接口使用


三、GenericAPIView的视图子类

GenericAPIView提供了5个视图拓展类以及9个视图子类,上面5个拓展类以及介绍了,下面来介绍视图子类

GenericAPIView的视图子类是依赖于视图拓展类的,视图子类内部本质就是继承了视图拓展,而且它内部为我们放在了对应的方法里面,所以我们只要使用了视图子类,便无需再写执行请求的方法。

ListCreateAPIView

执行查询所有数据与新增数据,我们可以继承ListCreateAPIView这个视图子类

源码

	'继承了GenericAPIView视图基类,以及2个视图拓展类'
	class ListCreateAPIView(mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView):
	    """
	    Concrete view for listing a queryset or creating a model instance.
	    """
	    '与我们使用的视图拓展类时代码相同,不过省略了我们定义请求方法的步骤'
	    def get(self, request, *args, **kwargs):
	    	 # 处理 HTTP GET 请求,调用 ListModelMixin 中的 list 方法
	        return self.list(request, *args, **kwargs)
	
	    def post(self, request, *args, **kwargs):
	    	# 处理 HTTP POST 请求,调用 CreateModelMixin 中的 create 方法
	        return self.create(request, *args, **kwargs)

使用方式:

	from rest_framework.generics import ListCreateAPIView
	class BookView(ListCreateAPIView):
		'ListCreateAPIView类里面具备了get方法与post方法,请求来的时候回调该类的get或者post请求'
		'而该类的内部又继承了GenericAPIView、ListModelMixin、CreateModelMixin'
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

使用效果:下面测试的是GET请求,而POST请求也是可以处理的,和我们上面效果相同。

在这里插入图片描述


RetrieveUpdateDestroyAPIView

该类内部具备了get(查询单个对象数据)、put、patch、delete方法。同样也对应继承了3个拓展类

源码

	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):
	    	# 处理 HTTP GET 请求,调用 RetrieveModelMixin 中的 retrieve 方法
	        return self.retrieve(request, *args, **kwargs)
	
	    def put(self, request, *args, **kwargs):
	    	# 处理 HTTP PUT 请求,调用 UpdateModelMixin 中的 update 方法
	        return self.update(request, *args, **kwargs)
	
	    def patch(self, request, *args, **kwargs):
	    	# 处理 HTTP PATCH 请求,调用 UpdateModelMixin 中的 partial_update 方法
	        return self.partial_update(request, *args, **kwargs)
	
	    def delete(self, request, *args, **kwargs):
	    	# 处理 HTTP DELETE 请求,调用 DestroyModelMixin 中的 destroy 方法
	        return self.destroy(request, *args, **kwargs)

通过内部具备了处理请求的方法,然后又继承了拓展类,所以我们只需要填写3行的固定代码即可实现这3个接口的使用

使用方式:

	from rest_framework.generics import RetrieveUpdateDestroyAPIView
	class BookDetailView(RetrieveUpdateDestroyAPIView):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

在这里插入图片描述

上面的两种方式就集成了5个接口的使用,且不需要我们手动定义处理请求的方法,而如果我们不想一下开启这么多接口,那么不妨使用下面的子类

  1. from rest_framework.generics import ListAPIView:具备了查询所有数据的get方法
  2. from rest_framework.generics import CreateAPIView:具备新增数据的post方法
  3. from rest_framework.generics import RetrieveAPIView:内部具备了查询单个对象的get方法
  4. from rest_framework.generics import UpdateAPIView:内部具备了修改单个对象的put方法
  5. from rest_framework.generics import DestoryAPIView:具备了删除单个对象的delete方法
  6. from rest_framework.generics import RetrieveUpdateAPIView:具备了查询、修改单个对象的get、put方法
  7. from rest_framework generics import RetrieveDestroyAPIView:具备了查询、删除单个对象的get、delete方法

总结:

综合上面我们使用过的两种方法,加上这些总共9个视图子类,而它们内部全部需要集成GenericAPIView作为基类,然后右继承了一些扩展类放在对应的请求方法里面,实现了我们只需要继承一些子类就能实现更多接口。

但是由于get请求无法区分是查询全部内容还是单个内容,所以我们不得不拆分两个类来处理,下面了解到的视图集可以很好的解决这个问题


四、视图集父类

在DRF中,视图集(Viewsets)是一种特殊的视图类,它将多个视图组合在一起,方便进行管理和维护,并提供通用的CRUD操作。

视图集可以分为两种类型:基于Model的视图集和基于普通类的视图集。基于Model的视图集允许快速创建一个RESTful API,它可以执行标准的CRUD操作。而基于普通类的视图集则允许更加灵活的定义自己的API逻辑。

在开始了解视图集之前,我们应该先了解一些视图集的父类


ViewSet

继承子APIViewViewSetMixin,作用也与APIView基本类似, 提供了身份认证、权限校验、流量管理等。

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典,如{'get':'list'}的映射处理工作,这表示:将get请求交给视图里面的list方法来处理

在ViewSet中,没有提供任何方法,需要我们自己手动定义方法

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 修改数据
  • destroy() 删除数据

ViewSet视图集类不在实现get()post()等方法,而是实现动作actionlist()create()等。
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。例子如下:

视图类

	from rest_framework.viewsets import ViewSet
	'ViewSet继承了ViewSetMixin, APIView'
	class BookViewSet(ViewSet):
	    def list(self,reqeust):
	        books = models.Book.objects.all()
	        ser = BookSerializer(intance=books,many=True)
	        return Response(ser.data)
	
	    def retrieve(self,request,pk=None):
	        try:
	            books = models.Book.objects.get(id=pk)
	        except models.Book.DoesNotExist:
	            return Response(status=status.HTTP_404_NOT_FOUND)
	
	        ser = BookSerializer(books)
	        return Response(ser.data)
		
		'定制login,使其是post方式就执行login'
		def login(self,request):
			return Response('login')

路由配置

	from django.urls import path
	from . import views
	
	urlpatterns = [
	    path('books/', views.BookViewSet.as_view({'get': 'list','post':'login'})),
	    path('books/<int:pk>', views.BookViewSet.as_view({'get': 'retrieve'})),
	]

注意:如果单个拎出来写ViewSetMixin需要把ViewSetMixin放到最前面。ViewSetMixin不能放在任何视图类后面,因为其他视图类中都继承了APIView而它里面有as_view,所以只能放最前面


GenericViewSet

使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView

	from rest_framework.viewsets import GenericViewSet
	from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,
	UpdateModelMixin,DestroyModelMixin
	
	'GenericViewSet继承了ViewSetMixin, generics.GenericAPIView'
	class BookView(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,
	UpdateModelMixin,DestroyModelMixin):
	
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

路由配置

	urlpatterns = [
	    path('books/', views.BookView.as_view({'get': 'list','post':'create'})),
	    path('books/<int:pk>', views.BookView.as_view({'get': 'retrieve','put':'update','delete':'destroy'})),
	]

此时虽然可以一个类实现5个接口,但是需要我们手动继承的类未免太多,所以我们接下来会了解到一种只需要继承一个类,实现5个接口,并且我们只需要定义一个类即可。


五、视图集子类

ModelViewSet

继承自GenericViewSet,同时也继承了五个拓展类ListModelMixinRetrieveModelMixinCreateModelMixinUpdateModelMixinDestoryModelMixin

视图层

	from rest_framework.viewsets import ModelViewSet
	class BookModelView(ModelViewSet):
	    queryset = models.Book.objects.all()
	    serializer = BookSerializer

ModelViewSet最主要的就是继承了GenericViewSet而它又继承了ViewSetMixin类那么只要我们继承了ViewSetMixin类,那么路由调用视图类的方式就要发生改变,上面两个实例就可以说明

如下:

	urlpatterns = [
	    path('books/', views.BookModelView.as_view({'get': 'list','post':'create'})),
	    path('books/<int:pk>', views.BookModelView.as_view({'get': 'retrieve','put':'update','delete':'destroy'})),
	]

ReadOnlyModelViewSet

只读的视图集,它具备了查询数据的接口,单个对象查询、所有对象查询。

继承自GenericAPIView,同时包括了:ListModelMixinRetrieveModelMixin


在视图集中自定义方法

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。

	from rest_framework.viewsets import ModelViewSet
	class BookModelView(ModelViewSet):
	    queryset = models.Book.objects.all()
	    serializer = BookSerializer

		def login(self,reqeust):
			'登录功能'
			return Response({'message':'登录成功'})

路由配置

	urlpatterns = [
	    path('login/',views.BookModelView.as_view({'get':'login'})),
	]

匹配上路由以后,如果是get请求则交给视图集里面的login方法来处理。


六、源码分析ViewSetMixin

ViewSetMixin不是视图类,它是让视图类继承后可以支持路由映射,它的核心就在as_view,因为它重写了as_view

	当请求来的时候,路由匹配成功后
		原来:执行APIView的as_view内的View(request)
		现在:执行ViewSetMixin的as_view内的View(request)

	
	class ViewSetMixin:
    """
    这是一个 mixin 类。
    重写了 `.as_view()` 方法,使其接受一个 `actions` 关键字参数,用于将 HTTP 方法绑定到资源的操作上。
    例如,要创建一个具体的视图,将 'GET' 和 'POST' 方法绑定到 'list' 和 'create' 操作上...
    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
    	# 检查是否提供了 `actions` 参数
    	'路由中as_view中必须传参数,并且传入的格式为字典,否则就会报错'
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
                            
		
		'路由匹配成功,执行view(request),request是django的request'
        def view(request, *args, **kwargs):
        	'''actions = {'get':'list','post':'create'}'''
        	'这里的self是视图类的对象'
            self = cls(**initkwargs)
            '设置实例的actions_map,属性为提供的actions字典'
            self.action_map = actions
            
         	'''
			遍历actions这个字典,为每个方法设置相应的方法
			如:
				第一次循环:method:get   action:list
				第二次循环:method:post  action:create
			'''
            for method, action in actions.items():
            	'self是视图类的对象中通过反射,查找action对应的方法,action第一次是list,去视图类 '
            	'handler就是视图类的对象中的list方法'
                handler = getattr(self, action)

				'反射修改(setattr有则修改无则添加):把method:get设置成list'
				'以后视图类的对象的get方法就变成list方法了'
                setattr(self, method, handler)

            self.request = request
            self.args = args
            self.kwargs = kwargs
			
			'调用dispatch方法并返回结果,后面就和正常的View一样的操作了'
            return self.dispatch(request, *args, **kwargs) # dispatch就是APIView的方法


	'扩展:以后只要继承了ViewSetMixin,视图类中可以写任意名字的方法,不用非要写请求方法同名的方法get、post、put'

总结

  1. 只要是继承了ViewSetMixin的视图类,路由写法就变了(重写了as_view)
    路由变成了需要传入字典映射方法:{'get':'list','post':'create'}
  2. 只要传入actions,以后访问get就是访问list,访问post就是访问create
  3. 其他执行跟之前一样
  4. 以后视图类类中的方法名,可以任意命名,只要在路由中做好映射即可(重要)
  5. ViewSetMixin:不是视图类,y要配合视图类一起,它重写了as_view方法,导致路由写法变了,路由改变的本质就是通过传的actions做映射{'get':"xxx"}
  6. 继承APIView+自动生成路由---->ViewSet
	class BookView(ViewSet,APIView): # ViewSet一定要写在前面
   		pass
  1. 继承GenericAPIView+自动生成路由---->GenericViewSet
	 class BookView(GenericViewSet,GenericAPIView)# GenericViewSet一定要写在前面
     	pass

七、DjangoFramework关系属性表

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值