DRF Mixin功能引入
在了解Mixin插件前,我们必须先了解什么是Mixin,具体可见这个文章
Mixin机制介绍
Mixin机制的引入,可以大大解放我们的双手,因为DRF通过Mixin已经帮我们把GET/POST/PUT…各种请求完全封装完成!
1. DRF - Mixin做了什么
Mixin其实就是一个插件功能,并且脱离于所有的子类数据,比如我们想给鸭子加一个游泳,给狗加一个游泳功能,那就创建一个SwimMixin,只要让雷继承于这个SwimMixin,就能实现游泳功能,DRF中也是类似的。
需求分析:
这是我们之前使用GenericAPIView实现的接口,尽管已经非常简洁
但是,里面的get方法我们还是要自己去get_serializer,POST还是要自己去save等等,还是比较麻烦
class BookListView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
"""
GET里面的ORM操作还是有些繁琐,需要自己去获取
"""
# self.get_serializer ---> 返回的就是 serializer_class实例化对象
serializer = self.get_serializer(instance=self.get_queryset(), many=True)
return Response(serializer.data)
def post(self, request):
"""
POST也要写一大堆东西才能save
"""
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_201_CREATED)
DRF中的Mixin直接为我们提供了 增删查改的各种方法,下面是一个使用Mixin的案例
2. 使用Mixin实现AuthorListView
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, ListModelMixin, RetrieveModelMixin,DestroyModelMixin
class AuthorListView(CreateModelMixin, ListModelMixin, GenericAPIView):
"""
因为ListView需要使用两种方式:
1.get请求获取全部数据
2.post创建数据
所以,直接继承CreateModelMixin(创建数据插件),ListModelMixin(列表模型插件)
"""
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, requset):
"""
使用了Mixin,我们直接返回一个self.list就可以
"""
return self.list(requset)
def post(self, request):
# 直接返回self.create就是实现了我们的创建数据
return self.create(request)
2.1 Mixin源码解析,为什么直接返回list/create就行
POST -> CreateModelMixin源码
class CreateModelMixin:
"""
Create a model instance.
翻译: 创建一个模型示例
"""
def create(self, request, *args, **kwargs):
"""
其实可以看到,这里的实现,和我们之前自己去create是类似的
所以其实就是帮我们直接把创建的方法封装起来了
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
# 最后返回的就是一个Response
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
GET -> ListModelMixin源码
class ListModelMixin:
"""
ListModelMixin和我们之前自己写的GET也是非常类似
"""
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)
2.2 Mixin有哪些?
DRF中的Mixins都存放在rest_framework.mixins库中
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, ListModelMixin, RetrieveModelMixin,DestroyModelMixin
-
CreateModelMixin 创建数据的Mixin(一般对应POST请求)
—> 返回 self.create(request) 即可
-
ListModelMixin 获取多个数据的Mixin(对应GET请求无主键id的情况)
—> 返回 self.list(request) 即可
-
UpdateModelMixin更新数据的Mixin(对于PUT/PATCH请求)
—> 返回 self.update(request) 即可
-
RetrieveModelMixin 获取单个数据的Mixin(对应GET请求有主键id的情况)
----> 返回 self.retrieve(request) 即可
-
DestroyModelMixin 删除数据的Mixin(对应DELETE请求)
----> 返回 self.destroy(request) 即可
2.3 Mixin案例
以下是基于Mixin实现的完整案例:
AuthorListView是对应POST和GET无主键id的请求
AuthorDetailView对应其他请求和GET有id请求
class AuthorListView(CreateModelMixin, ListModelMixin, GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, requset):
return self.list(requset)
def post(self, request):
return self.create(request)
class AuthorDetailView(DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, requset,pk):
return self.retrieve(requset)
def delete(self, request,pk):
return self.destroy(request)
def put(self, request,pk):
return self.update(request)
3. Mixins + APIView混合使用
上面的虽然已经非常非常的简易了,但是其实可以发现,我们每次写的时候,还是有很多重复的地方
- 每次都得继承一大堆东西,太长了
- 每次都得定义下get/post/put…但是里面逻辑都一样,完全没必要
!!而DRF考虑我们懒人需求,所以就增加了Mixins和APIVies混用的封装!!!
我们现在这里明白需求,我们的同一个请求接口,其实对应两种情况
- 路由传参有pk参数(有主键ID) -> GET多个/PUT/PATCH/DELETE
- 没有pk参数(无主键ID) -> POST创建/GET单个
所以,其实,就是分成了两种类,一种类是有 GET/POST并且不需要pk
另一种类就是有GET/DELETE/PUT还得要pk
而DRF为我们直接将这两种情况分别封装出来!
from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
3.1 ListCreateAPIView类
看名字就能看出来,他是一个包含创建+List返回的APIView,那么我们看看他的源码是什么样
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
其实一眼就可以看出来,他就是帮我们继承了两个Mixin还有GenericAPIView
然后帮我们写个get和post
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
所以,ListCreateAPIView其实就是帮我们做了我们觉得麻烦的事情,有了 它以后,我们的代码再次简化!
class AuthorListView(ListCreateAPIView):
"""
好了,就这三行代码,让你把一个视图写完了!
"""
queryset = Author.objects.all()
serializer_class = AuthorSerializer
就这么简单,So Easy的实现了你的接口!!
3.2 RetrieveUpdateDestroyAPIView类
这和3.1一样,一眼看出来,就是查询单个/更新/删除+APIView的封装类
说白了就是PUT/PATCH啥的,我们也不用写了,直接继承完事儿
继承后的代码如下:
class AuthorDetailView(RetrieveUpdateDestroyAPIView):
"""
还是三行代码,带你又完成了一个接口!
"""
queryset = Author.objects.all()
serializer_class = AuthorSerializer
现在,我两个ORM对应的视图,一共20行代码,就完成了接口部分的开发,我们只需要去专注于Model和序列化器就够了!
3.3 其他问题!
现在有小朋友问了,万一我这个接口,我不想要PATCH,老板说这个多余,不要这个,怎么办
有没有什么快速解决办法??
必须有啊,我们首先要明白,为什么RetrieveUpdateDestroyAPIView能直接帮我们直接把三个全部实现了,是因为人家源码中继承了3个
那你自己直接继承两个不就完事儿了??PATCH的那个你直接不要了就实现了需求!
但是!!!DRF帮我们写好了!
- RetrieveUpdateAPIView : 只有更新和查询单个,没有删除
- RetrieveAPIView :只有查询单个,连更新都没有
- UpdateAPIView : 只有更新,没有删除和查询
- 以此类推,全部都支持,想怎么换怎么换!!
4. 重要提醒
虽然这样写非常的爽,十几行代码完成了你所有的需求,但是!!!!
我们这样写出来的代码可定制度实在太低,如果要实现其他额外的需求,这些写就不够用了
这种只适用于标准接口的开发,而复杂逻辑的开发,还是需要用到基类APIView
APIView我在前面的文章也有讲过,他是实现这些高级封装类的基础类
只有看明白了APIView,才能高度定制自己的Django应用