【Django知识点】【Django Rest Framework 学习】fbv开发、cbv开发、APIView、GenericAPIView、Viewset、...

前言

  1. 本文内容主要讲解介绍Django Rest Framework框架,结合如下django项目学习食用:Github仓库地址
  2. 所有请求的调试,均通过postman实现

基本概念

FBV:function based views

学习参照Github仓库的fbv文件夹

特点

  1. 无需继承类

  2. 基于函数形式的视图开发

  3. fbv默认为get方法,如果提供post或其他类型方法,需要自行if判断

代码实现

# urls.py
urlpatterns = [
    path('book/list/', views.book_list),
]

# views.py
def book_list(request):
    book_name = '射雕英雄传'
    
    return render(request, 'book_list.html', {'book_name':book_name})
  1. 客户端访问路由:http://127.0.0.1:8000/book/list/时,触发book_list函数

  2. 服务端执行book_list函数的内容,处理后返回结果

CBV:class based views

特点

  1. 基于类的视图函数调用
  2. 用函数将各种请求方式分开,符合面向对象的开发
  3. CBV的本质是FBV

介绍

  1. Django Rest Framework 框架所封装的方法都是CBV形式的视图
  2. 下文将逐步介绍 Django Rest Framework 框架,体会面向对象开发的魅力

CBV_View【Django】— — Django的原生View

学习参照Github仓库的cbv_view文件夹

流程追踪,源码解析

# urls.py

urlpatterns = [
    url(r'^books/$', views.BooksView.as_view()),
    url(r'^books/(?P<pk>\d+)$', views.BookView.as_view()), 
]

客户端访问特定路由,激活自定义类的as_view函数

# views.py

class BooksView(View):
    def get(self, request):
		pass

    def post(self, request):
		pass

BooksView类中没有as_view函数,从其父类View里面寻找

@classonlymethod	# 允许在不实例化类的时候,调用类方法:as_view
def as_view(cls, **initkwargs):

    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        self.setup(request, *args, **kwargs)
        if not hasattr(self, "request"):
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
        return self.dispatch(request, *args, **kwargs)

    return view

在删除了不必要的部分后,可以看出as_view函数的关键在于:其内部定义了一个view函数,并且将该函数作为返回值

即:views.BooksView.as_view() → views.BooksView.view() → views.BookView.dispatch()

self = views.BookView

def dispatch(self, request, *args, **kwargs):
    # Try to dispatch to the right method; if a method doesn't exist,
    # defer to the error handler. Also defer to the error handler if the
    # request method isn't on the approved list.
    if request.method.lower() in self.http_method_names:
        handler = getattr(
            self, request.method.lower(), self.http_method_not_allowed
        )
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

# 补充
http_method_names = [
    "get",
    "post",
    "put",
    "patch",
    "delete",
    "head",
    "options",
    "trace",
]

可以看到dispatch函数通过getattr()方法,将"get"、"post"等请求与函数get()、post()联系在一起

  1. request.method.lower() = “get”
  2. handler = self.get() = views.BookView.get()

注意点

  1. 若是需要在每个函数执行对应功能前,加入公共的操作,就可以在BookView中重写dispatch函数。但要注意,重写了dispatch函数需要给定返回结果

as_view()可选参数:**initkwargs

# urls.py
urlpatterns = [
    url(r'^books/$', views.BooksView.as_view(para='xxx')),
]


# views.py
class BooksView(View):
    para = None

    def get(self, request):
		pass

    def post(self, request):
		pass

可以在URL配置中传递额外的参数给视图类,并在视图类中使用这些参数进行处理。请注意,参数名称需要与视图类的构造函数参数名称相匹配。

as_view传入的参数必须在BooksView中定义好,不可出现未定义的类属性变量


CBV_APIView【DRF】— — 基于Django的原生View

学习参照Github仓库的cbv_apiview文件夹

流程追踪,源码解析

同于View,但是在内容上,处理流程更加复杂

视图处理上的差异

# 数据处理上的区别

# view
def post(self, request):
    
    # ===== 差异所在 =====
    data = request.body.decode()
    data_dict = json.loads(data)

    print(type(request.body), type(request.body.decode()), type(data_dict))

    # 验证数据
    btitle = data_dict.get('btitle')
    bpub_date = data_dict.get('bpub_date')
    if btitle is None or bpub_date is None:
        return JsonResponse({'errors': '错误信息'}, status=400)

    # 保存数据
    book = BookInfo.objects.create(btitle=btitle, bpub_date=bpub_date)
    
    
# APIView
def post(self, request):
    
    # ===== 差异所在 =====
    ser = BookInfo_Serializers(data=request.data)
    resp = {}
    if ser.is_valid():
        ser.save()
        resp['code'] = 201
        resp['code'] = ser.data
    else:
        print(ser.errors)
        resp['code'] = 401
        resp['code'] = ser.errors
    return Response(resp)

支持序列化器操作,在Request和Response上都有区别

View和APIView的区别

APIView继承自View

APIViewView
传入视图函数中的是Rest Framework的Request对象【优点-1】是Django的原生HttpRequest对象【不足-1】
视图方法返回Rest Framework的Response对象【优点-2】是Django的原生JsonResponse对象

【优点-1】

  1. 数据解析:DRF提供了Parser解析器。在接收到请求后会**自动根据Content-Type指明的请求数据类型(如JSON、表单等)**将请求数据进行parse解析,解析为类字典对象保存到Request对象中
  2. 序列化机制:无论前端发送哪种格式的数据,都可以以统一的方式读取数据,并且与Serializer集成,可方便处理POST、PUT请求
  3. 完善的异常处理机制(没深入了解)
  4. 身份验证机制(没深入了解)

【不足-1】

  1. 需要自行对传入的数据解析:decode()、json()、…

【优点-2】

  1. 功能扩展:Response的父类和JsonResponse的父类中都有HttppResponse,Response的功能要更加完善
  2. 序列化支持:与Serializer集成,可直接将Serializer的实例作为Response响应的数据
  3. 响应格式统一:Respnse响应格式符合Restful API设计规范:状态码、响应数据、其他信息(分页、错误信息)。且这些信息在不同的视图里面是统一的,便于用户处理
  4. 响应修改灵活:Response提供多种方法、属性,便于访问和修改响应的各个部分

Serializer和ModelSerializer的学习(暂跳过)


CBV_GenericAPIView【DRF】:基于DRF的APIView

学习参照Github仓库的cbv_genericapiview_modelserializers文件夹

APIView和GenericAPIView的区别

GenericAPIView继承自APIView

APIView回顾

  1. 提供了DRF定义的Resuest对象和Response对象,可以结合序列化器,方便快捷的进行数据处理响应
  2. 在该过程中,不管是get()、post()或put()方法,都会用到序列化器和query_set对象,并且是重复使用

GenericAPIView新增

  1. 在APIView的基础上,进一步扩展功能、封装;简化代码实现

视图处理

class Book_Genericapiview_ModelSerializers(GenericAPIView):
    
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
    '''获取单一和更新和删除'''
    def get(self, request, pk):

        # # 方法1:APIView常规操作
        # try:
        #     books = BookInfo.objects.get(id=pk)
        # except:
        #     return Response({'errors':'错误信息, 数据不存在'}, status=400)
        
        
        # 方法2:Genericapiview提供的操作
        ser = self.get_serializer(instance=self.get_object(), many=False)

        return Response(ser.data)  

通过定义类基本属性:queryset和serializer_class,并结合GenericAPIView定义的类方法,实现序列化、单一数据查询、多数据查询等操作


等价操作

GenericAPIViewAPIView
self.get_queryset()BookInfo.objects.all()
self.get_object()BookInfo.objects.get(id=pk)
self.get_serializer_class()BookSerializer
self.get_serializer(instance=self.get_queryset(), many=True)BookSerializer(instance=self.get_queryset(), many=True)

CBV_GenericAPIView_Mixin【DRF】

学习参照Github仓库的cbv_genericapiview_minin文件夹

Mixin和GenericAPIView的联系

并列为自定义视图类提供父类继承关系

GenericAPIView回顾

  1. 尽管封装了queryset和serializer_class类,简化了操作。但是对于get、post、put等方法还是需要自己去实现有关查询、新建、更新、删除等具体实现逻辑

Mixin扩展

  1. Mixin封装了5个类:
    1. ListModelMixin,提供list函数,用以查询所有的对象数据
    2. CreateModelMixin,提供了create函数,用以新建对象数据
    3. RetrieveModelMixin,提供了retrieve函数,用以查询特定id或其他字段的数据
    4. UpdateModelMixin,提供了update函数,用于更新特定id的对象数据
    5. DestroyModelMixin,提供了destory函数,用于删除特定id的对象数据

视图处理

# viewspy
class Books_Genericapiview_Minin(ListModelMixin, CreateModelMixin, GenericAPIView):
    # GenericAPIView规定要写的类属性
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
    '''获取所有和保存'''
    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)
    
# 找寻方法也是从左到右,继承顺序很关键
class Book_Genericapiview_Minin(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
    # 自定义destory函数,改变删除逻辑
    def destroy(self, request, pk):
        books = self.get_object()
        books.is_delete = True
        books.save()
        return Response({})
    
    '''获取单一和更新和删除'''
    def get(self, request, pk):
        return self.retrieve(request, pk)
    
    def put(self, request, pk):
        return self.update(request, pk)
    
    # Minin里面定义的delete是物理删除
    def delete(self, request, pk):
        return self.destroy(request, pk)

CBV_GenericAPIView_Mixin_Repackaging【DRF】

学习参照Github仓库的cbv_genericapiview_minin_repackaging文件夹

Mixin和Mixin_Repackaging的联系

Mixin扩展回顾

  1. 尽管封装了5个类,写好了增删改查查操作的具体实现逻辑,但是仍需要在定义的时候声明get()、post()等方法

Mixin_Repackaging扩展

  1. 根据实现逻辑,对五个类进行组合,并在内部写好get、post等方法:
    1. ListCreateAPIView,提供list函数、create函数,并且内部定义绑定了get函数、post函数
    2. RetrieveUpdateDestroyAPIView,提供了retrieve、update、destory函数,并且在内部实现了get函数、update函数、destory函数

视图处理

# views.py
class Books_Genericapiview_Minin_Repackaging(ListCreateAPIView):
    # GenericAPIView规定要写的类属性
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
            
    
class Book_Genericapiview_Minin_Repackaging(RetrieveUpdateDestroyAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
    # 自定义destory函数,改变删除逻辑
    def destroy(self, request, pk):
        books = self.get_object()
        books.is_delete = True
        books.save()
        return Response({})
    

CBV_Viewset【DRF】

学习参照Github仓库的cbv_viewset文件夹

回顾

  1. 逐步封装:View、APIView、GenericAPIView、GenericAPIView_Mixin、GenericAPIView_Mixin_Repackaging
  2. 视图函数的编写越来越简洁,增删改查查功能越来越集中
  3. 但:不管封装到那一步,关于查操作:查询单个数据和查询全部数据,都是通过两个视图类来做区分
  4. Viewset在此基础上,进行进一步的封装:
    1. 重写despatch函数,平且可以通过as_view()函数传参,来将get、post、put等请求,绑定到具体的函数名称上
    2. 在类视图编写上,做到:一个类视图实现增删改查查所有操作

源码解析

class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    """
    pass

Viewset继承自ViewSetMixin, views.APIView(即上文所描述的APIView)

class ViewSetMixin:
    """
    This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the 'GET' and 'POST' methods
    to the 'list' and 'create' actions...

    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        """
        Because of the way class based views create a closure around the
        instantiated view, we need to totally reimplement `.as_view`,
        and slightly modify the view function that is created and returned.
        """
        pass

ViewSetMixin 重写了 as_view函数,并且实现了 Http请求(get、post、put等)与 函数操作(自定义list函数,create函数)的绑定

视图处理

# views.py
class Books_Viewset(ViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer

    def get_all(self, request):
        pass

    def add_object(self, request):
        pass

    def get_object(self, request, pk):
        pass

    def update_object(self, request, pk):
        pass

    def delete_object(self, request, pk):
        pass
    
    
# urls.py
urlpatterns = [
    url(r'^books_viewset/$', views.Books_Viewset.as_view({
        'get': 'get_all',
        'post': 'add_object'
    })),
    # 有名分组:P<pk>
    url(r'^books_viewset/(?P<pk>\d+)$', views.Books_Viewset.as_view({
        'get': 'get_object',
        'put': 'update_object',
        'delete': 'delete_object'
    }))
]
  1. 将增删改查查在一个视图函数里面实现,但是在urls里面,还是需要通过定义两个url路由才行
  2. 但是基础的viewset不提供任何的具体操作函数,需要自己编写

CBV_GenericViewSet_Mixin【DRF】

学习参照Github仓库的cbv_viewset文件夹

GenericViewSet_Mixin和ViewSet的区别

ViewSet回顾

  1. 不提供具体的资源操作方法,需要自定义函数并于Http 请求绑定

GenericViewSet_Mixin新增

  1. 借助Mixin类提供的默认create、update等资源操作方法,通过在urls路由里面进行绑定,实现http请求与资源操作函数的一一对应

视图处理

# views.py
class Books_Viewset_Pro(GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
    
# urls.py
urlpatterns = [
    url(r'^books_viewset_pro/$', views.Books_Viewset_Pro.as_view({
        'get': 'list',
        'post': 'create'
    })),
    # 有名分组:P<pk>
    url(r'^books_viewset_pro/(?P<pk>\d+)$', views.Books_Viewset_Pro.as_view({
        'get': 'retrieve',
        'put': 'update',
        'delete': 'destroy'
    }))
]
  1. 在urls里面,进行http请求和默认资源操作函数的绑定
  2. 但是还有优化空间:视图函数的继承、urls路由绑定的简化

CBV_ModelViewSet【DRF】

学习参照Github仓库的cbv_viewset文件夹

ModelViewSet和GenericViewSet_Mixin的区别

GenericViewSet_Mixin回顾

  1. 视图函数的继承实现有待优化、url路由请求方法和资源操作函数的绑定有待优化

ModelViewSet新增

  1. 大爹来了,究级封装

视图处理

# views.py
class Books_Viewset_Pro_Max(ModelViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookSerializer
    
    
# urls.py
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'books_viewset_pro_max', views.Books_Viewset_Pro_Max, basename='books_viewset_pro_max')

urlpatterns = [
    url(r'^books_viewset_pro_max/$', views.Books_Viewset_Pro_Max.as_view({'get':'list', 'post':'create'})),
    # 有名分组:P<pk>
    url(r'^books_viewset_pro_max/(?P<pk>\d+)$', views.Books_Viewset_Pro_Max.as_view({'get':'retrieve', 'put':'update', 'delete':'destroy'})),

]

urlpatterns += router.urls
  1. 视图函数处理,简洁优美;url路由绑定,同样简洁优美
  2. http请求方法与资源操作函数的绑定可以通过路由分配,也可以通过as_view()函数传参

注意事项

  1. 凡单个数据操作:books_viewset_pro_max/pk/
    1. 基于router分配路由,最后一个斜杠一定要有,不然会落入匹配get请求的url中
    2. 基于as_view()字典分发的路由,则不需要最后一个斜杠

DRF CBV开发总结

注释

  1. get(请求全部数据)、get(请求单个数据)、post、put、delete,这5个http request请求缩写为:ggppd
  2. 将queryset和serializer写进类属性,简称为:写入q、s类属性
CBV模式视图路由总结
View(原生Django,非DRF)自定义ggppd及实现逻辑(写两个)调用as_view()函数匹配(不传参,写两个)通过despatch函数实现请求与资源操作函数的绑定
APIView自定义ggppd及实现逻辑(写两个)调用as_view()函数匹配(不传参,写两个)通过DRF封装提供的Request请求和Response响应,并结合序列化器,简化数据操作
GenericAPIView写入q、s类属性调用as_view()函数匹配(不传参,写两个)将queryset和serializer写进类属性,并提供调用方法,避免重复使用序列化器
自定义ggppd及实现逻辑(写两个)
GenericAPIView_Mixin写入q、s类属性调用as_view()函数匹配(不传参,写两个)通过定义Mixin类,提供基本的资源操作函数:list、create、retrieve、update、destory
自定义ggppd,实现逻辑由Mixin类提供(写两个)
GenericAPIView_Mixin_Repackaging仅写入q、s类属性(写两个)调用as_view()函数匹配(不传参,写两个)http请求和资源处理函数的绑定在类里面实现,完成进一步封装
Viewset写入q、s类属性调用as_view()函数匹配(传参,写两个)通过as_view传参,完成请求资源与操作函数的绑定
自定义ggpppd及实现逻辑(写一个)
GenericViewSet_Mixin仅写入q、s类属性(写一个)调用as_view()函数匹配(传参,写两个)http请求和资源处理函数的绑定在类里面实现,完成进一步封装。且该情况下的传参,post只能对应create,除非重写
ModelViewSet仅写入q、s类属性(写一个)调用as_view()函数匹配(传参,写两个)
通过router路由分发(不传参,写一个)可通过自定义资源操作函数及请求方式和url,在增删改查查的基础上,继续扩展新的url

DRF CBV开发总结-2

View(原生Django,非DRF)

维度描述
继承关系无继承
视图自定义ggppd及实现逻辑(写两个视图类)
路由调用as_view()函数匹配(不传参,写两个)
总结通过despatch函数实现请求(ggppd)与资源操作函数的绑定:post请求对应的资源操作函数就是 post(*args, **kwargs)

APIView(DRF)

维度描述
继承关系View
视图自定义ggppd及实现逻辑(写两个视图类)
路由调用as_view()函数匹配(不传参,写两个)
总结通过DRF封装提供的Request请求和Response响应,并结合序列化器,简化数据操作

GenericAPIView(DRF)

维度描述
继承关系APIView
视图写入q、s类属性;自定义ggppd及实现逻辑(写两个视图类)
路由调用as_view()函数匹配(不传参,写两个)
总结将queryset和serializer写进类属性,并提供调用方法:self.get_object、self.get_queryset(),避免重复使用序列化器

GenericAPIView_Mixin(DRF)

维度描述
继承关系ListModelMixin, CreateModelMixin, GenericAPIView
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView
视图写入q、s类属性;自定义ggppd,实现逻辑由Mixin类提供(写两个视图类)
路由调用as_view()函数匹配(不传参,写两个)
总结通过定义Mixin类,提供基本的资源操作函数:list、create、retrieve、update、destory

GenericAPIView_Mixin_Repackaging(DRF)

维度描述
继承关系ListCreateAPIView:ListModelMixin, CreateModelMixin, GenericAPIView
RetrieveUpdateDestroyAPIView:RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView
视图仅写入q、s类属性(写两个视图类)
路由调用as_view()函数匹配(不传参,写两个)
总结http请求和资源处理函数的绑定在类里面实现,完成进一步封装

Viewset(DRF)

维度描述
继承关系ViewSetMixin, APIView
视图写入q、s类属性;自定义ggppd及实现逻辑(写一个视图类)
路由调用as_view()函数匹配(传参,写两个)
总结通过as_view传参,完成请求资源与操作函数的绑定

GenericViewSet_Mixin(DRF)

维度描述
继承关系GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
视图仅写入q、s类属性(写一个视图类)
路由调用as_view()函数匹配(传参,写两个)
总结http请求和资源处理函数的绑定在类里面实现,完成进一步封装。且该情况下的传参,post只能对应create,除非重写

ModelViewSet(DRF)

维度描述
继承关系ModelViewset
视图仅写入q、s类属性(写一个视图类)
路由调用as_view()函数匹配(不传参,写两个)
调用router,进行路由分配(不传参,写一个)
总结可通过自定义资源操作函数及请求方式和url,在增删改查查的基础上,继续扩展新的url
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值