一、2个基类
# django RESTframework提供的视图的主要工作
控制序列化器的执行(检验、保存、转换数据)
控制数据库查询的执行
# 基类:
#1. APIView:
from rest_framework.views import APIView
继承了原生的django的View
#2. GenericAPIView:
from rest_framework.generics import GenericAPIView
继承了APIView
# 类中主要的数据和内置可调用的方法:
- queryset = models.Book.BookModelSerializer
- serializer_class = serializer.BookModelSerializer
- get_queryset:获取配置的queryset
- get_object:路由中的分组字段必须是pk(源码中就是根据pk到数据库取值的)
- get_serializer:获取配置的序列化类
# 总结:
如果视图和models没有关系(没有数据库相关操作),就继承APIView
有数据库操作就继承GenericAPIView,用到里面的queryset和serializer_class
代码示例:
### views.py视图层:
from rest_framework.generics import GenericAPIView
class BookGenericView(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = 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(request.data)
if ser.is_valid():
ser.save()
return Response('创建成功')
return Response(ser.errors)
class BookDetailGenericView(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
# 路由有名分组中的pk将被存到kwargs中
def get(self, request, *args, **kwargs):
obj = self.get_object()
# 这里的get_object()方法会自动将kwargs中的pk取出来,到数据库get取值,取不到就返回错误信息
ser = self.get_serializer(obj)
return Response(ser.data)
def post(self, request, *args, **kwargs):
obj = self.get_object()
ser = self.get_serializer(obj, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def delete(self, request, *args, **kwargs):
self.queryset.filter(id=kwargs.get('pk')).delete()
return Response('删除成功')
### urls.py
urlpatterns = [
path('books_ger/', views.BookGenericView.as_view()),
re_path('^books_ger/(?P<pk>\d+)', views.BookDetailGenericAPIView.as_view()),
]
### serializer.py:
from app01 import models
from rest_framework import serializers
class BookModelSerializer(serializer.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
鉴于上面的视图层代码有大量的重复代码,我们可以稍微整合一下,自行封装几个类让基类继承、调用里面的方法:
class ListView():
def get(self, request, *args, **kwargs):
obj = self.get_queryset()
ser = self.get_serializer(obj, many=True)
return Response(ser.data)
class CreateView():
def post(self, request, *args, **kwargs):
ser = self.get_serializer(request.data)
if ser.is_valid():
ser.save()
return Response('创建成功')
return Response(ser.errors)
# 让基类BookGenericView继承一下上面两个有单一特殊功能的类
class BookGenericView(GenericAPIView, ListView, CreateView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
# 也可以让BookDetailGenericView继承三个整合特殊功能的类,这里就不再累赘了(因为源码中有)
class BookDetailGenericView(...):
pass
二、5个视图扩展类
from rest_framework.mixins import ...
- CreateModelMixin :内置了一个create方法,创建一个新的对象
- ListModelMixin :内置list方法,获取所有的对象
- RetrieveModelMixin :内置retrieve方法,获取一个对象
- UpdateModelMixin :内置update方法,修改一个对象
- DestroyModelMixin :内置destroy方法,删除一个对象
代码示例:
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin
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、put等方法的写法,似乎换个其他的类来,也是一样的写法,标点符号都不带换的,所以,继续封装!
于是就有了9个子类视图
三、9个视图子类
from rest_framework.generics import CreateAPIView,ListAPIView,ListCreateAPIView
from rest_framework.generics import UpdateAPIView,RetrieveAPIView,DestroyAPIView,RetrieveUpdateAPIView,RetrieveDestroyAPIView,RetrieveUpdateDestroyAPIView
# 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,DestroyAPIView,GenericAPIView,有get方法获取一条,有delete方法删除
# RetrieveUpdateAPIView:
继承了RetrieveModelMixin,UpdateModelMixin,GenericAPIView,有get方法获取一条,有put/patch方法修改数据
# RetrieveUpdateDestroyAPIView:
继承了RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView,有get方法获取一条数据,put,patch修改,delete删除
于是,代码就变得尤其精简:
from app01.serializer import BookModelSerializer
from app01 import models
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
class BookGenericView(ListCreateAPIView):
# ListCreateAPIView 里面有get、post方法
obj = models.Book.objects.all()
serializer_class = BookModelSerializer()
class BookDetailGenericView(RetrieveUpdateDestroyAPIView):
# RetrieveUpdateDestroyAPIView 里面有get、put、delete方法
obj = models.Book.objects.all()
serializer_class = BookModelSerializer()
即便是这样,还是有高度重合的代码,但是碍于路由的配置对有无pk的识别,这里暂时需要要写两个类
于是视图集应运而生~
四、视图集
# ViewSetMixin:
重写了as_view方法,加了一个actions参数,将视图扩展类中的方法反射出去调用
# ViewSet:
继承了ViewSetMixin和APIView
# GenericAPIViewSet
继承了ViewSetMixin,generic.GenericAPIView
# ModelViewSet
from rest_framework.viewsets import modelViewSet
继承了mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet
于是就有了五个视图扩展类的方法:list、create、retrieve、update、destroy,
通过actions参数就把内存地址响应地赋值给get、post、get、put、delete来调用
# ReadOnlyModelViewSet:
继承了mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet
只能查所有和查单条数据,不能使用新增、修改、删除的方法
# 可以配一个自定义的视图集,继承需要的指定的视图扩展类,只提供相应的接口功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3FqoSBW-1605684473551)(https://gitee.com/cuicheng688/img/raw/master/img/image-20201106205134784.png)]
使用了视图集以后,上面的五个功能就可以写的异常精简了:
from rest_framework.viewsets import ModelViewSet
class BookSetView(ModelViewSet):
obj = models.Book.objects.all()
serializer_class = BookModelSerializer
需要配一下路由:
urlpatterns = [
# 使用视图集必须要传一个字典
path('bookset/', views.ModelViewSet.as_view({'get': 'list', 'post': 'create'})),
re_path(r'bookset/(?P<pk>\d+)',
views.ModelViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]
五、action的使用
# 继承了ViewSetMixin类
# 视图类的方法中就会有个action属性,print(self.action)可以打印类的当前方法名
class BookView(ViewSet):
def login(self, request, *args, **kwargs):
print(self.action)
return Response('login success')
# 只要继承了ViewSetMixin ,路由的配置就发生了变化,只需要写映射即可:
# 路由配置:
urlpatterns = [
path('book_mix/', views.BookView.as_view({'post':'login'}))
]
六、路由的使用
1、配置路由的方式
#1. 最原始的
path('books/', views.BookAPIView.as_view()),
#2.继承了ViewSetMixin的视图类
path('books_set/', views.BookSetView.as_view({'get':list,'post':'create'})),
#3.继承了ViewSetMixin的视图类
可以自动生成url
2、自动生成路由
# 继承了ViewSetMixin的视图类,写路由可以自动生成
# SimpleRouter
# DefaultRouter(继承了SimpleRouter,与SimpleRouter没有本质上的区别,比SimpleRouter多了一个根路径,访问根路径就会提示可以访问的路由)
## 路由写法:
from rest_framework import routers
# 实例化得到一个对象(一般使用SimpleRouter)
router = routers.SimpleRouter()
# 注册进路由
router.register('books', views.BookSetView)
# 把自动生成的路由配置到urlpatterns中(两种方法)
-1.常用:
urlpatterns += router.urls
#(如果想在自动生成的路径前面加路径,需要写在注册router.register里面)
# 如:router.register('v1/books', views.BookSetView)
-2.不常用,(了解)可以直接在路由之前加路径
urlpatterns = [
re_path(r'v1/', include(router.urls))
]
- 使用DefaultRouter (访问根路径的路径就会提示可访问的路径):
- 使用DefaultRouter访问不存在的路径时返回的页面提示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84xiZ6Lr-1605684473554)(https://gitee.com/cuicheng688/img/raw/master/img/image-20201109013245026.png)]
- 使用SimpleRouter访问不存在的路径时返回的页面提示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UNVn3sgQ-1605684473555)(https://gitee.com/cuicheng688/img/raw/master/img/image-20201109012429696.png)]
3、action
# 自动生成路由的时候,由于视图类中还有其他方法,是无法自动生成路由的
# 自动生成其他方法路由的方法——加装饰器action:
参数:
- methods:何种请求方式会触发被装饰器函数的执行
- detail:为True,就是基于带id的路由生成的; 为False,就是基于不带id的路由生成的
用法示例:
@action(methods=['get'],detail=True)
代码示例:
views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
class BookSetView(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
@action(methods=['get'], detail=False)
def register(self, request, *args, **kwargs):
print(self.action)
return Response('login success')
@action(methods=['post'], detail=True)
def login(self, request, *args, **kwargs):
print(self.action)
return Response('login success')
urls.py
from app01 import views
from rest_framework import routers
router = routers.SimpleRouter()
# router = routers.DefaultRouter()
router.register('books', views.BookSetView, basename='book')
print(router.urls)
urlpatterns = [
]
urlpatterns += router.urls