1.api_view和APIView
首先api_view和APIView对应了最基本的FBV和CBV,api_view是一个装饰器用来确保视图中收到request实例,并且将上下文添加到相应中,而在drf中写CBV最先要继承的就是APIView,可以通过代码示例来掌握两者的使用的区别
from .serializers import SnippetsSerializer
from .models import Snippet
@api_view(["GET",])
def snippets_list(request):
snippets=Snippet.objects.all()
snippets_serializer=SnippetsSerializer(snippets,many=True)
return Response(snippets_serializer.data,status=status.HTTP_200_OK)
@api_view(["GET","PUT","DELETE"])
def snippet_detail(request,pk):
try:
snippet=Snippet.objects.get(id=pk)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_400_BAD_REQUEST)
if request.method=="GET":
snippet_serializer=SnippetsSerializer(snippet)
return Response(snippet_serializer.data,status=status.HTTP_200_OK)
if request.method=="PUT":
snippet_serializer=SnippetsSerializer(snippet,request.data)
if snippet_serializer.is_valid():
snippet_serializer.save()
return Response(snippet_serializer.data,status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
if request.method=="DELETE":
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
其中api_view中填入的是fbv的请求方法,如果未传入参数默认使用的是GET方法
CBV的基本用法如下
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class SnippetList(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
从上所看,drf中fbv和cbv的写法同django本身的写法大同小异,只是使用的装饰器和继承的类不同。
2.高级组合
当然drf作为一个成熟的框架其功能远远不止如此,drf的最终目标还是通过最少的代码实现原来一样的功能,使用CBV的一大好处就是我们可以轻松的可重用的行为,我们可以看看如何进一步优化我们的代码
from snippets.models import Snippet
from snippets.serializers import SnippetsSerializer
from rest_framework import mixins
from rest_framework import generics
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetsSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
我们在此引入了mixins和generics模块,我们可以看出上面的代码同样实现get和post方法,但是我们在get和post方法内部并没有去实现过于繁杂的代码,仅仅是调用了一个list和post方法,这样我们就可以将相应的http请求定到对应的操作上,如get请求就对应了list方法而post请求就对应了create方法。我们在这个CBV中只需要指明我们需要使用的对象查询集合和serializer类。我们可以看看list和create的内部实现原理
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)
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)
可以看出在list方法内部跟我们在第一部分实现的get方法大致相同,首先获取我们在类中执行的queryset和serializer,最后将对象序列化。至于GenericAPIView它继承的是我们先前使用APIView.
虽然到了这里我们的代码已经足够精简但是还是可以进一步优化,如下。
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetsSerializer
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetsSerializer
我们这里使用的是ListCreateAPIView,我们可以看下其内部源码
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)
我们可以看到其内部自动的将http请求方法对应到我们的对象操作方法上,至此我们只需要在CBV中指明queryset和serializer_class即可。