DRF入门-ViewSet
1. 前言
之前我们使用普通的或者封装的APIView开发接口时,总是需要分两类情况处理
- 有主键 /user/20/ (id=20的用户)
- 无主键 /user/ (实现get所有或者post创建)
我们总是需要分两种接口去写,然后还要进行两次类的封装,其实代码的冗余还是比较高,那有没有办法,直接写一个类就能实现呢?
ViewSet类就是DRF给我们的解决方案!
2. ViewSet引入
2.1 ViewSet能实现什么?
其实ViewSet实现的,就是让我们原本必须按照规定,定义的post/get/delete方法,可以随心所欲的进行定义,不再需要必须起那个名字
比如,我希望我的函数名这样起,这样他就能实现一个类满足有无主键的两种需求
from rest_framework.viewsets import ViewSet # 导入ViewSet
class BookViewSet(ViewSet):
"""
继承ViewSet
"""
def get_all_data(self, request): # 对应get无pk方法(查询多个)
return Response('返回所有书籍')
def create_data(self, request): # 对应post创建方法
return Response('创建一个书籍')
def get_one_data(self, request, pk): # 对应get有pk方法(查询单个)
return Response(f'返回pk={pk}的书籍')
def update_data(self, request, pk): # 对应put/patch有pk更新方法
return Response(f'更新pk={pk}的书籍')
def delete_date(self, request, pk): # 对应delete有pk删除
return Response(f'删除pk={pk}的书籍')
所以我们只要按这个思路去走,就能够再次简化原本的代码!
但是,我们光进行定义,程序也不知道POST/GET到底该去调用哪个方法
对于定义了ViewSet的类,我们需要在路由中传入as_view()方法的时候传入字典!
传入格式:
“请求方法名”:“实际执行的函数名”
urls.py
from django.urls import path,re_path
from BookManage.views import *
urlpatterns = [ path("booknew/<int:pk>/",BookView.as_view({"get":"get_one_data","delete":"delete_date","put":"update_data"})),
# 对于有pk的,我们 get -> get_one_data | delete -> delete_date | put -> update_data
path("booknew/",BookView.as_view({"get":"get_all_data","post":"create_data"})),
# 对于没有pk的,get -> get_all_data | post -> create_data
]
此时我们进行请求就可以发现,使用一个类就可以完成两种接口的请求。
GET http://127.0.0.1:8000/booknew/1/
---> "返回pk=1的书籍"
DELETE http://127.0.0.1:8000/booknew/1/
---> "删除pk=1的书籍"
GET http://127.0.0.1:8000/booknew/ # 无pk
---> "返回所有书籍"
3. ModelViewSet引入
3.1 ModelViewSet源码解析
其实ViewSet与APIView类似,他也有对应的GenericViewSet,并且也支持继承Mixin
所以为了我们的方法便,这里直接引入ModelViewSet
其实ModelViewSet本质上就是帮我们继承了所有的Mixins,让我们只需要在定义路由的时候,分别定义不同的方法就够了!
ModelViewSet源码:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
# 它就是帮我们把五种Mixins全部继承,并且继承了GenericViewSet
# GenericViewSet和我们的GenericAPIView几乎一样,就是多了可以自定义不同方法使用哪个函数
pass
所以使用ModelViewSet后,我们可以直接把之前用两个类定义的代码,拿过来换成一个类,然后直接重新写一个路由即可!
使用ModelViewSet定义之前的BookListView和BookDetailView
3.2 案例实现
views.py类定义
from rest_framework.viewsets import ModelViewSet
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get_all_data(self, request): # 对应get无pk方法(查询多个)
return self.list(request)
def create_data(self, request): # 对应post创建方法
return self.create(request)
def get_one_data(self, request, pk): # 对应get有pk方法(查询单个)
return self.retrieve(request)
def update_data(self, request, pk): # 对应put/patch有pk更新方法
return self.update(request)
def delete_date(self, request, pk): # 对应delete有pk删除
return self.destroy(request)
urls.py定义路由
from django.urls import path,re_path
from BookManage.views import *
urlpatterns = [ path("booknew/<int:pk>/",BookView.as_view({"get":"get_one_data","delete":"delete_date","put":"update_data"})),
path("booknew/",BookView.as_view({"get":"get_all_data","post":"create_data"})),
]
请求结果示例:
GET http://127.0.0.1:8000/booknew/2/
{
"id": 2,
"name": "嫌疑人X的现身",
"pub_date": "2006-10-03",
"author": {
"id": 2,
"name": "东野圭吾",
"introduce": "东野圭吾(ひがしの けいご,Higashino Keigo),1958年2月4日出生于日本大阪,日本推理小说作家,毕业于大阪府立大学电气工学专业,之后在汽车零件供应商日本电装担任生产技术工程师。代表作有《放学后》《秘密》《白夜行》《以眨眼干杯》《神探伽利略》《嫌疑人X的献身》《预知梦》《湖畔》等。"
},
"price": "40.00"
}
4. 路由再次简化!
虽然我们这样非常方便,但是我们每次去定义路由的时候,都要去把定义的路由重新写一大串,非常的啰嗦,麻烦!
DRF封装了视图集(ModelView)配合他的路由route,可以让我们把双手彻底解放掉!!
urls.py创建默认路由
from rest_framework import routers # 导入路由函数
route = routers.DefaultRouter() # 创建路由
route.register('book', viewset=BookView)
# 注册路由,viewset是我们创建的ViewSet视图(必须是继承自viewset/派生类的视图,不能是APIView)
# 注册的第一个参数填写名称即可
# 填写book则会关联 /book/ 以及 /book/<pk>/
urlpatterns = []
urlpatterns += route.urls # 将创建的路由添加到列表
--------也可以这样--------
urlpatterns = [
path('',include(route.urls)), # 用include进行路由分发,可以用到名称空间
]
这时候我们定义下ViewSet即可
from rest_framework.viewsets import ModelViewSet
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
......
# 下面啥也不用写,只要继承了 ModelViewSet,就不用管了!
# 除非需要自定义其他请求!