21. drf视图组件

一、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的识别,这里暂时需要要写两个类

于是视图集应运而生~

四、视图集

image-20201106210047208

# 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
    只能查所有和查单条数据,不能使用新增、修改、删除的方法

# 可以配一个自定义的视图集,继承需要的指定的视图扩展类,只提供相应的接口功能
image-20201106203457077

image-20201106204230919

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 (访问根路径的路径就会提示可访问的路径):

image-20201109012916596

  • 使用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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值