Loading

DRF之视图

一、基于APIView的五个接口

首先,APIView是Rest Framework提供的所有视图的基类,继承了Django的View

其次,APIView和View的不同在于:

  • 他们传入视图方法中的request对象不是同一个,APIView的是Request对象,View的是HttpResponse对象。
  • APIView返回的是Response对象,是符合前端Accept要求的格式
  • 在dispatch分发前会进行身份认证,权限检查,流量控制

五个接口实例:

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books


# 基于APIView
class BooksView(APIView):
    # 查所有
    def get(self, request):
        books = Books.objects.all()
        books_ser = BooksSerializers(instance=books, many=True)
        return Response(books_ser.data)
	
    # 增加
    def post(self, request):
        books_ser = BooksSerializers(data=request.data)
        if books_ser.is_valid():
            books_ser.save()
        return Response(books_ser.data)


class BookView(APIView):
    # 查单个
    def get(self, request, pk):
        print(pk, type(pk))
        book = Books.objects.filter(pk=pk).first()
        book_ser = BooksSerializers(instance=book)
        return Response(book_ser.data)

	# 修改
    def put(self, request, pk):
        book = Books.objects.filter(pk=pk).first()
        book_ser = BooksSerializers(instance=book, data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'message': book_ser.errors})

	# 删除
    def delete(self, request, pk):
        Books.objects.filter(pk=pk).delete()
        return Response({'message': '删除成功'})
    
    
# urls.py
path('books/', views.BooksView.as_view()),
re_path(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),

二、基于GenericAPIView的五个接口

首先,GenericAPIView继承了APIView,并增加了操作序列化器和数据库查询方法

并提供了两个属性和两个方法

queryset:指要传的queryset对象

get_queryset():返回queryset对象

serializer_class:指明视图使用的序列化器,也就是哪个序列化类来序列化这堆数据

get_serializer():返回序列化器类

get_object():返回视图所需的模型类数据对象

源码如下:

# from rest_framework.generics import GenericAPIView
class GenericAPIView(views.APIView):
    queryset = None				# 指要传的queryset对象
    serializer_class = None		# 指明视图使用的序列化器,也就是哪个序列化类来序列化这堆数据
    ......
    
	def get_queryset(self):
    	......
        queryset = self.queryset
        # 判断传入的queryset是不是QuerySet的对象
        if isinstance(queryset, QuerySet):
            queryset = queryset.all()	# 是就加all()——>Book.objects.all()
        return queryset		# 返回queryset对象


	def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        
        # 向序列化器对象的context属性补充三个数据,
        # request,当前视图的请求对象
        # format,当前请求的类视图对象
        # view,当前请求期望返回的数据格式
        kwargs.setdefault('context', self.get_serializer_context())
        return serializer_class(*args, **kwargs)	# 返回序列化器类

五个接口实例:

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books


# 基于GenericAPIView
class BooksView(GenericAPIView):
    queryset = Books.objects
    serializer_class = BooksSerializers
	
    # 查所有
    def get(self, request):
        books = self.get_queryset() # 相当于Books.objects.all()
        books_ser = self.get_serializer(instance=books, many=True)
        return Response(books_ser.data)
	
    # 新增
    def post(self, request):
        books_ser = self.get_serializer(data=request.data)
        if books_ser.is_valid():
            books_ser.save()
        return Response(books_ser.data)


class BookView(GenericAPIView):
    queryset = Books.objects
    serializer_class = BooksSerializers

	# 查单个
    def get(self, request, pk):
        book = self.get_object()
        book_ser = self.get_serializer(instance=book)
        return Response(book_ser.data)

	# 修改
    def put(self, request, pk):
        book = self.get_object()
        book_ser = self.get_serializer(instance=book, data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'message': book_ser.errors})

	# 删除
    def delete(self, request, pk):
        self.get_object().delete()
        return Response({'message': '删除成功'})

三、GenericAPIView的五个视图括展类

因为之前代码的处理流程重复代码太多,所以可以使用相应的扩展类来减少编写的代码量。

而这五个扩展类需要搭配GenericAPIView父类,并且还要调用GenericAPIView提供的序列化器和数据查询方法。

ListModeMixin

列表视图扩展类(类似于查询所有),提供了快速获取所有列表的视图,并且该方法会对数据进行过滤和分页,成功返回200

源代码:

class ListModelMixin:
    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)	# 返回序列化后的数据

CreateModeMixin

创建视图扩展类(创建数据),提供了快速创建资源的视图,成功返回201,失败400

原代码:

class CreateModelMixin:
    # 创建模型实例
    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 {}

RetrieveModelMixin

详情视图扩展类(查询单个),返回一个存在的数据对象,成功返回201,失败404

原代码:

class RetrieveModelMixin:
    def retrieve(self, request, *args, **kwargs):
        # 获取对象,并检查对象的权限
        instance = self.get_object()
        # 序列化
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

UpdateModelMixin

更新视图扩展类(更新数据),快速更新一个存在的数据对象,也可实现局部更新 partial_update() 成功返回201,失败400

源代码

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

DestroyModelMixin

删除视图扩展类(删除数据),快速删除一个存在的数据对象,成功返回204,不存在返回404。

源代码

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

五个视图扩展类的使用实例

from rest_framework.generics import GenericAPIView
from app01.ser import BooksSerializers
from app01.models import Books
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin


# 基于GenericAPIView的五个视图扩展类
class BooksView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Books.objects
    serializer_class = BooksSerializers

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

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


class BookView(GenericAPIView, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin):
    queryset = Books.objects
    serializer_class = BooksSerializers

    def get(self, request, pk):
        return self.retrieve(request)


    def put(self, request, pk):
        return self.update(request)


    def delete(self, request, pk):
        return self.destroy(request)

四、GenericAPIView的视图子类

二话不说先来看其中一个源码!!!其他的子类都是一样的原理

源码

# 首先它继承了CreateModelMixin创建视图扩展类
class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):	
    # 并且它提供了post方法,那我们就不用在视图中写post方法了,这里直接写好了直接调用self.create
    # 只需要传入对象和序列化器就可以了
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

实例:

from rest_framework.generics import ListAPIView, UpdateAPIView
from rest_framework.generics import CreateAPIView, DestroyAPIView, RetrieveAPIView
from app01.ser import BooksSerializers
from app01.models import Books


# 基于GenericAPIView的五个视图子类
class BooksView(ListAPIView, UpdateAPIView):    # 获取所有,修改一个
    queryset = Books.objects
    serializer_class = BooksSerializers


# 获取一个,更新一个,删除一个
class BookView(CreateAPIView, DestroyAPIView, RetrieveAPIView):
    queryset = Books.objects
    serializer_class = BooksSerializers

GenericAPIView的视图子类除了这五个还有四个,也就是这五个方法的其他组合:

  • ListCreateAPIView
    • 提供get、post方法,是 ListAPIView 和 UpdateAPIView 的组合
  • RetrieveDestroyAPIView
    • 提供get、delete方法,是 RetrieveAPIView 和 DestroyAPIView 的组合
  • RetrieveUpdateAPIView
    • 提供get、update方法,是 RetrieveAPIView 和 UpdateAPIView 的组合
  • RetrieveUpdateDestroyAPIView
    • 提供get、update、delete方法,是 RetrieveAPIView 、 UpdateAPIView 和 DestroyAPIView 的组合

五、ModelViewSet

首先它继承了 GenericViewSet,并且还有 ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

ModelViewSet五个接口

先来看一下它的使用:

# views.py
from rest_framework.viewsets import ModelViewSet
from app01.ser import BooksSerializers
from app01.models import Books

class BooksView(ModelViewSet):		# 五个接口都有
    queryset = Books.objects
    serializer_class = BooksSerializers
    
    
# urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 当路径匹配,又是get请求,会执行BooksView的list方法
    path('books/', views.BooksView.as_view({'get': 'list', 'post': 'create'})),
    re_path(r'^book/(?P<pk>\d+)/', views.BooksView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]

源码分析

由于 ModelViewSet 继承了 GenericViewSet ,并且 GenericViewSet 又继承了 ViewSetMixin,ViewSetMixin中又重写了 as_view 方法, 也就是说当路径匹配上了会执行这里重写的 as_view 方法。

分析: as_view 方法

@classonlymethod
def as_view(cls, actions=None, **initkwargs):
    
	...

    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        
        # 核心代码,所有路由中只要配置了对应关系,比如{'get': 'list'},当get请求来,就会执行list方法
        for method, action in actions.items():
            # 遍历我们传入的对应关系
            # method = get
            # action = list
            
            # 并且通过反射获取list方法的内存地址
            handler = getattr(self, action)		# handler已经是list的内存地址
            
            # 点拦截(对象.get = list)
            setattr(self, method, handler)
            
        # 当循环完毕之后,对象.get:对应list方法,对象.post:对应create方法

        ...

        # 后面照常继续
        return self.dispatch(request, *args, **kwargs)

继承ViewSetMixin的视图类

由上面的分析可以得出,我们也可以直接继承 ViewSetMixin 写视图,如下:

from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books


class BooksView(ViewSetMixin, APIView):
    def get_all_books(self, request):
        book_list = Books.objects.all()
        book_ser = BooksSerializers(book_list, many=True)
        return Response(book_ser.data)
    
    
# urls.py
# 当匹配上了路由,又是get请求,就调用get_all_books方法
path('books/', views.BooksView.as_view({'get': 'get_all_books'})),
posted @ 2021-07-08 21:33  Mr-Yang`  阅读(103)  评论(0编辑  收藏  举报