1、- 继承ModelViewSet
(即:拥有基本视图类的所有方法)
从它的源码可以看到,它不仅继承了(1)五大拓展类,同时也继承了(2)GenericViewSet
而GenericViewSet
又继承了ViewSetMixin
(提供了自定义视图函数)通用视图类GenericAPIView
(提供序列化和反序列化操作方法(查询集类属性、实例化序列化器类))
- 视图
views.py
代码:
from rest_framework.viewsets import ModelViewSet
from .serializers import *
from books_test.models import *
# 继承ModelViewSet
class BooksView(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
- 路由映射原由:
- 原生的Django框架的路由映射规则是请求方式为——GET–>视图get方法,POST–> post方法(即:请求方式是什么,路由器就会自动去找请求方式小写的视图方法)
- 问:当我们继承
ModelViewSet
时,DRF内部提供的视图方法为list
、create
和update
等等。那么它是如何知道当请求方式为GET时,去找对应的list方法呢? - 答:重写路由映射规则(重写
as_view()
方法,里面传入字典
进行映射)
- 路由映射:如下图代码
from django.contrib import admin
from django.urls import path,re_path
from books_test.views import *
urlpatterns = [
path('admin/', admin.site.urls),
# 获取多个对象
re_path(r'^books/$', BooksView.as_view({'get':'list', 'post': 'create'})),
# 获取单一资源:GET + /books/(?P<pk>\d+)/ = self.get
re_path(r'^books/(?P<pk>\d+)/$',BooksView.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})),
]
- 2、前面我们已经知道:
ModelViewSet
继承了GenericViewSet
,而GenericViewSet
又继承了ViewSetMixin
(提供了自定义视图函数)。所以我们可以自定义视图方法latest
,如下代码:
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from .serializers import *
from books_test.models import *
class BooksView(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
# 请求路径:/books/latest/
def latest(self, request, *args, **kwargs):
book = self.queryset.latest('bpub_date') # 最新发布的书
serializer = self.get_serializer(instance=book)
return Response(data=serializer.data)
- 路由映射:
re_path(r'^books/latest/$', BooksView.as_view({'get': 'latest'})),
指定get
方法–>latest
视图方法
from django.contrib import admin
from django.urls import path, re_path
from books_test.views import *
urlpatterns = [
path('admin/', admin.site.urls),
# 获取多个对象
re_path(r'^books/$', BooksView.as_view({'get': 'list', 'post': 'create'})),
re_path(r'^books/latest/$', BooksView.as_view({'get': 'latest'})),
# 获取单一资源:GET + /books/(?P<pk>\d+)/ = self.get
re_path(r'^books/(?P<pk>\d+)/$',BooksView.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})),
]
-
postman测试:
-
3、如果每创建一个视图函数,我们都需要去重写
as_view()
路由映射,太麻烦,因此我们选择自动映射路由
-
路由映射三步走
from django.contrib import admin
from django.urls import path, re_path
from rest_framework.routers import SimpleRouter
from books_test.views import *
urlpatterns = [
path('admin/', admin.site.urls),
]
# 1、实例化路由对象
router = SimpleRouter()
# 2、注册视图集(指定映射规则,如:路由前缀)
router.register(prefix='books', viewset=BooksView)
# 3、把生成的路由映射router.urls -->[re_path(), re_path()....],加入路由列表urlpatterns中
# urlpatterns.extend(router.urls)
urlpatterns += router.urls
-
4、
SimpleRouter
和DefaultRouter
的区别 -
这里有个问题:
-
4.1
SimpleRouter
自动生成的路由不包括自定义的视图函数latest
只会生成:(指定的prefix:books
前缀拼接xxx路径,单独访问127.0.0.1:8000会返回404报错)
(1)^books/$ [name='bookinfo-list']
–>相当于re_path(r'^book/$')
(2)^books/(?P<pk>[^/.]+)/$ [name='bookinfo-detail']
–>相当于re_path(r'^book/<pk>/$')
-
4.2
DefaultRouter
自动生成的路由也不包括自定义的视图函数latest
生成的路由和SimpleRouter
的区别为:
-
127.0.0.1:8000/books.json可以得到原生的
json数据
-
根路由,访问127.0.0.1:8000不会返回404报错,而是返回下面的页面
-
5、action方法装饰器
-
作用:修饰自定义视图函数,使之能够进行自动路由映射
-
因为上面两种自动映射路由方法都不会映射自定义视图方法所以我们选择装饰器。
@action(methods=['get'], detail=False, url_path='latest')
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from .serializers import *
from books_test.models import *
# action作用:修饰自定义视图函数,使之能够进行自动路由映射
from rest_framework.decorators import action
class BooksView(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
# methods: 映射的请求方式
# url_path: 路径拼接尾缀,默认值是函数名字
# detail:
# True ---> 路径拼接是: 前缀(prefix) + pk正则分组 + 尾缀(url_path)
# False ---> 路径拼接是: 前缀(prefix) + 尾缀(url_path)
@action(methods=['get'], detail=False, url_path='latest')
def latest(self, request, *args, **kwargs):
book = self.queryset.latest('bpub_date') # 最新发布的书
serializer = self.get_serializer(instance=book)
return Response(data=serializer.data)
- 测试: