Django REST framwork 提供的视图的主要作用:
1.控制序列化器的执行(检验、保存、转换数据)
2.控制数据库查询的执行
一、两个视图基类
先上结论:
APIView:如果跟models没有关系(没有数据库相关操作),就继承它
GenericAPIView:有关数据库操作,queryset 和serializer_class
1.APIView
继承了原生Django的View,需要我们自己写对应请求方法的代码,比如
class BookAPIView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
ser = serializer.BookModelSerializer(book_list, many=True)
return Response(ser.data)
def post(self, request):
ser = serializer.BookModelSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.error)
class BookDetailAPIView(APIView):
def get(self, request, pk):
book = models.Book.objects.get(id=pk)
ser = serializer.BookModelSerializer(book)
return Response(ser.data)
def put(self, request, pk):
book = models.Book.objects.get(id=pk)
ser = serializer.BookModelSerializer(book, data=request.data)
if ser.is_valid():
ser.save()
return Response('修改成功')
def delete(self, request, pk):
models.Book.objects.filter(id=pk).delete()
return Response('删除成功')
2.GenericAPIView
继承了APIView,考虑到使用APIView时,当我们写多个类,变化的代码只有调用的myserializers类和自己的表模型,所以将变量提取了出来。
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
get_queryset:获取配置的queryset
get_object:单条记录查询时使用,路由中的分组字段必须是pk
get_serializer:获取配置的序列化类
class BookGenericView(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
def get(self, request, *args, **kwargs):
obj = self.get_queryset()
ser = self.get_serializer(obj, many=True)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser=self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response('成功')
class BookDetailGenericAPIView(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
def get(self, request, *args, **kwargs):
obj = self.get_object()
ser = self.get_serializer(obj)
return Response(ser.data)
def put(self, request, *args, **kwargs):
obj = self.get_object()
ser = self.get_serializer(obj, data=request.data)
if ser.is_valid():
ser.save()
return Response('修改成功')
def delete(self, request, *args, **kwargs):
# self.get_object().delete() 或者下面的方法
self.queryset.filter(id=kwargs.get('pk')).delete()
return Response('删除成功')
二、五个视图扩展类
当我们利用GenericAPIView类把变量提取出去后,会发现其实对于不同的接口我们其实内部代码是一样的,所以进行了封装,即把每个请求方法都提取出来,封装成单独的一个类。
from rest_framework.mixins import ...
CreateModelMixin:create方法创建一条
DestroyModelMixin:destory方法删除一条
ListModelMixin:list方法获取所有
RetrieveModelMixin:retrieve获取一条
UpdateModelMixin:update修改一条
class BookGenericView(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookDetailGenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
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 delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
三、九个子类视图
子类视图是对扩展类的进一步封装,因为我们上一部分还是有重复代码,既然这些请求方法进来之后,执行逻辑一样,那么我就再进一步封装。
五个扩展类,分为群get,post,单get,put,delete。在我们常见类中,一般是群get,post一个类,另外三个方法一个类,所以各自组合,正好九种子类视图,即2+1,3+2+1,共有九种组合方式
#9个子类视图(rest_framework.generics)
CreateAPIView:继承CreateModelMixin,GenericAPIView,有post方法,新增数据
DestroyAPIView:继承DestroyModelMixin,GenericAPIView,有delete方法,删除数据
ListAPIView:继承ListModelMixin,GenericAPIView,有get方法获取所有
UpdateAPIView:继承UpdateModelMixin,GenericAPIView,有put和patch方法,修改数据
RetrieveAPIView:继承RetrieveModelMixin,GenericAPIView,有get方法,获取一条
ListCreateAPIView:继承ListModelMixin,CreateModelMixin,GenericAPIView,有get获取所有,post方法新增
RetrieveDestroyAPIView:继承RetrieveModelMixin,DestroyModelMixin,GenericAPIView,有get方法获取一条,delete方法删除
RetrieveUpdateAPIView:继承RetrieveModelMixin,UpdateModelMixin,GenericAPIView,有get获取一条,put,patch修改
RetrieveUpdateDestroyAPIView:继承RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView,有get获取一条,put,patch修改,delete删除
以下为最全的方式九个子类视图比较灵活,可以任意组合
from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
class BookGenericView(ListCreateAPIView):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
class BookDetailGenericAPIView(RetrieveUpdateDestroyAPIView):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
四、视图集
当我们已经封装到了子类视图时,就会发现,代码的重复又出现了,以上我们写的两个类其实代码一样,所以又进行了一次封装。一般情况下我们使用ModelViewSet或者ReadOnlyModelViewSet,但是视图集不仅仅包含这两个。
from rest_framework.viewsets import ModelViewSet # 包含五种请求方法
from rest_framework.viewsets import ReadOnlyModelViewSet # 只有查询,单记录查和群查
class BookSetView(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
1. ViewSet
继承自APIView
与ViewSetMixin
,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:’list’})的映射处理工作。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
2.GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView
,所以还需要继承GenericAPIView
。
GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView
与ViewSetMixin
,在实现了调用as_view()时传入字典(如{'get':'list'}
)的映射处理工作的同时,还提供了GenericAPIView
提供的基础方法,可以直接搭配Mixin扩展类使用。
举例:
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
url的定义
urlpatterns = [
path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
re_path("students7/(?P<pk>d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),
]
3.ModelViewSet
继承自GenericViewSet
,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
4.ReadOnlyModelViewSet
继承自GenericViewSet
,同时包括了ListModelMixin、RetrieveModelMixin。
5.源码分析
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。因我们现在把所有动作都封装了,所以在单get和群get请求的时候会出现问题,因为两个都是get请求,且请求打到同一个类中,在这里,框架作者使用了非常巧妙的方法来解决这个问题。解决的方案是在路由里配上对应的方法。代码如下
path('books_set/', views.BookSetView.as_view({'get':'list','post':'create'})),
re_path('^books_set/(?P<pk>d+)', views.BookSetView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
在原生的类中as_view方法肯定是不支持这种做法的,所以ModelViewSet一定重写了as_view()方法
五、路由的使用
一般如果使用了drf的视图类,那么都会配合路由组件一起使用,因为路由组件可以帮我们自动生成路由的路径
1.路由组件的简单使用
# 自动生成路由,以下两个使用较多
SimpleRouter与DefaultRouter
# 继承了ViewSetMixin的视图类,以后写路由,可以自动生成,写在urls.py中
from rest_framework import routers
# 实例化得到一个对象
router = routers.SimpleRouter()
# 注册进路由
router.register('books', views.BookSetView)
# 把自动生成的路由配置到urlpatterns中
-urlpatterns += router.urls # 或者下面的方式
-re_path(r'v1/', include(router.urls))
# 这里的v1指的是在生成的路径前都加上v1这个路径,比如原来的login,现在变成了v1/login
2.配置路由的方式
# 三种方式
1.最原始的
-path('books/', views.BookAPIView.as_view()),
2.ViewSetMixin的视图类
-path('books_set/', views.BookSetView.as_view({'get':'list','post':'create'}))
3.ViewSetMixin的视图类
-自动生成,上面讲的
3.action
-当自动生成路由的时候,由于视图类中还有其它方法,是无法自动生成路由的
-加action装饰器:
-methods:什么请求方式会触发被装饰函数的执行
-detail:是True是基于带id的路由生成的,如果是False,是基于不带id的路由生成的
-@action(methods=['get'], detail=True)