一、视图三部曲
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 = BookSerializers
def 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_frameworkview.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)