2.3 mixins模块源码分析
既然讲到这里,使用了mixins,那么不得不讲讲源码: class CreateModelMixin(object):增加 class ListModelMixin(object):展示数据列表 class RetrieveModelMixin(object):展示单条数据 class UpdateModelMixin(object):更新单条数据 class DestroyModelMixin(object):删除单条数据
在分析源码前,我们先来了解多重继承的一个知识点
class A: def a(self): print("A") self.b() class B: def b(self): print("B") class D(A,B): pass d = D() d.a()
会输出AB 类D多重继承AB,类D实例对象d.a()时候,如果在类A中找不到b()方法,它就会在多重继承的AB类中,横向寻找b方法,结果在类B中找到,然后输出
2.3.1 mixins.ListModelMixin
def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs)
我们可以看到,这个方法直接返回了self.list(request, args, *kwargs),它会理所应当的去多重继承的父类中寻找方法,然后跟踪代码,我们可以看到进入了:
class ListModelMixin(object): """ List a queryset.==> 列表页获取 """ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) # 这是一个分页功能,如果在viewset中设置了pagination_class,那么这里就会起作用 # 获取当前页的queryset,如果不存在分页,返回None page = self.paginate_queryset(queryset) if page is not None: # 分页不为空,那么不能简单的执行Response(serializer.data) # 还需要将相关的page信息序列化在进行响应 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)
ListModelMixin一般用来获取列表页,大多数情况下比较简单,不需要重写相关的方法。
2.3.2 mixins.CreateModelMixin
创建视图扩展类,提供create(request, args, *kwargs)方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。
def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
我们可以看到,这个方法直接返回了self.create(request, args, *kwargs),它会理所应当的去多重继承的父类中寻找方法,然后跟踪代码,我们可以看到进入了:
class CreateModelMixin(object): """ Create a model instance ==>创建一个实例 """ def create(self, request, *args, **kwargs): # 获取相关serializer serializer = self.get_serializer(data=request.data) # 进行serializer的验证 # raise_exception=True,一旦验证不通过,不再往下执行,直接引发异常 serializer.is_valid(raise_exception=True) # 调用perform_create()方法,保存实例 self.perform_create(serializer) # 用户Token验证,并且通过return {'Location': str(data[api_settings.URL_FIELD_NAME])}保存一个临时头部数据,最后 # 将序列化的数据,响应状态码(201 Created:请求已经被实现)、头部一并返回即可 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 {}
由上图可以看出这个类的一个逻辑,其中,perform_create( )对serializer直接进行save保存,当在一些情境下,我们需要对perform_create( )进行重写。 那么我们现在可能有一个下面的需要:
假设现在有一个course课程model,里面维持了一个数,记录课程收藏数,还存在一个用户收藏userfav的model(应当有一个外键指向course),当一个用户对课程进行收藏,理论上现在post进来的应该是userfav的instance,显然,我们还需要对相应course的收藏数进行+1。 这个时候,我们就需要重写perform_create( )方法!
def perform_create(self, serializer): # 重写save的逻辑 instance = serializer.save() course = instance.course course.fav_num += 1 course.save()
2.3.3 mixins.RetrieveModelMixin
详情视图扩展类,提供retrieve(request, args, *kwargs)方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。
queryset = Snippet.objects.all() serializer_class = SnippetSerializer def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs)
执行get方法也是从GenericAPIView多重继承类中获取get_object,然后进行序列化,最后 return Response(serializer.data)返回即可.
class RetrieveModelMixin(object): """ Retrieve a model instance.==> 获取某一个对象的具体信息 """ def retrieve(self, request, *args, **kwargs): # 一般访问的url都为/obj/id/这种新式 # get_object()可以获取到这个id的对象 # 注意在viewset中设置lookup_field获取重写get_object()方法可以指定id具体对象是什么~! instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data)
2.3.4 mixins.UpdateModelMixin
更新视图扩展类,提供update(request, args, *kwargs)方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request, args, *kwargs)方法,可以实现局部更新。
成功返回200,序列化器校验数据失败时,返回400错误。
def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs)
UpdateModelMixin实现逻辑基本整合了Create以及Retrieve,先得到具体的实例,再对其进行验证以及保存,如果需要对更新这个逻辑进行自定义,那么需要重写perform_update( )方法,而尽量少去重写update( )
class UpdateModelMixin(object): """ 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) 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.3.5 mixins.DestroyModelMixin
删除视图扩展类,提供destroy(request, args, *kwargs)方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。
def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
通过pk(或者其他标识)获取要删除的数据,然后instance.delete()删除,最后返回return Response(status=status.HTTP_204_NO_CONTENT)
class DestroyModelMixin(object): """ 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()
2.3.6 GenericAPIView
在APIView源码分析,分析了APIView的大概执行流程,我们先看下多重继承类中的
generics.GenericAPIView,看看它跟APIView有何关联?点开源码,原来GenericAPIView类是APIView的子类,继而扩充了一些其他的方法,我举出如下几个方法
get_queryset:提供方法,用来获取request请求封装完毕的结果集 get_object:获取单条数据 get_serializer:获取序列化后的数据 get_serializer_class:获取需要序列化的model类 get_serializer_context:获取序列化的数据,定义了某种格式的字典 paginator:分页器
2.3.7 generics
class SnippetList(generics.ListCreateAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer
跟踪源码可以看到generics.ListCreateAPIView也是继承自mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView
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)
2.3.8 总结
接口一般继承 generics 模块里面的以下这些类就行了,如果对get 、post 等方法有特殊的要求可以自己继承后重写 get、post 方法。
generics 模块里面有 :
2.3.8.1 GenericAPIView
不提供 get 、 post 等方法, 要跟 mixins 中的模块一起继承才能使用 get 、 post 等方法
2.3.8.2 CreateAPIView
提供 post 方法,用来【创建数据】。 继承自 mixins.CreateModelMixin 和 GenericAPIView
2.3.8.3 ListAPIView
提供 get 方法,用来【获取数据集合】。 继承自 mixins.ListModelMixin 和 GenericAPIView
2.3.8.4 RetrieveAPIView
提供 get 方法,用来【获取单条数据】。 继承自 mixins.RetrieveModelMixin 和 GenericAPIView
2.3.8.5 DestroyAPIView
提供 delete 方法,用来【删除单条数据】 继承自 mixins.DestroyModelMixin 和 GenericAPIView
2.3.8.6 UpdateAPIView
提供 put 和 patch 方法, 用来【修改单条数据】 继承自 mixins.UpdateModelMixin 和 GenericAPIView
2.3.8.7 ListCreateAPIView :
提供 get 和 post 方法,用来【获取数据集合】 和 【创建数据】 继承自 mixins.ListModelMixin,mixins.CreateModelMixin,和GenericAPIView
2.3.8.8 RetrieveUpdateAPIView
提供 get 、 put 和 patch 方法,用来【获取】和【修改】单条数据 继承自 mixins.RetrieveModelMixin,mixins.UpdateModelMixin,和GenericAPIView
2.3.8.9 RetrieveDestroyAPIView
提供 get 和 delete 方法,用来【获取】和【删除】单条数据 继承自 mixins.RetrieveModelMixin,mixins.DestroyModelMixin,和GenericAPIView
2.3.8.10 RetrieveUpdateDestroyAPIView
提供 get 、 put、 patch 和 delete 方法,用来【获取】、【修改】和【删除】单条数据 继承自 mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,和GenericAPIView
mixins相对比较好理解,本篇只是简要的分析了源码的内容以及各个mixins的逻辑,最重要的还是学会去重写它们相关的方法。 一般情况下,当我们在操作某一个model的时候,涉及到另外一个model中数据的修改,那么就需要对这个mixins下执行save的逻辑的方法进行重写。