73-DRF的封装:APIView类及五大模块

目录

  • 一、drf框架的封装特点

    • 1、APIView类
    • 2、请求模块
    • 3、解析模块
    • 4、响应模块
    • 5、渲染模块(了解)
    • 6、异常模块

一、drf框架的封装特点

drf一定要在settings中注册和配置

如何自定义配置drf:

# settings.py
# drf框架自定义配置
REST_FRAMEWORK = {
    # 全局配置解析类:适用于所有视图类
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],
    # 全局配置渲染类:适用于所有视图类
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        # 'rest_framework.renderers.BrowsableAPIRenderer',  # 上线后尽量关闭
    ],
    # 异常模块:异常处理函数
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}

drf的模块封装非常的规范,名词定义非常的严谨

import rest_framework
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from rest_framework.pagination import PageNumberPagination
from rest_framework.settings import APISettings
from rest_framework.parsers import JSONParser
from rest_framework.filters import OrderingFilter

1、APIView类

APIView的请求生命周期

  1. urls.py中路由调用自定义资源类的as_view方法;
  2. 系统会从自定义资源类中搜索as_view方法,但发现没有,于是会从自定义资源类的父类APIView中搜索as_view方法,发现可以找到,于是调用;
  3. 因为APIView类继承View类,所以其实它是重写了as_view方法;
  4. 在APIView类中重写as_view方法:
    1. 判断了当前自定义类实例化出来的对象是否是一个queryset对象,如果是就报错;
    2. 如果不是,就会获取父类(View类)的as_view方法的函数地址并赋值给名为view的变量;
    3. 将自定义类及其实例化对象的参数计入变量view的名称空间,让其可以调用,然后用函数将view即将通过的csrf认证组件禁用,再将处理后的view变量返回给路由,这样客户端访问该url时,就会直接调用这个处理后的view变量。
    # rest_framework的views.py文件
    def as_view(cls, **initkwargs):
        """
            Store the original class on the view function.
    
            This allows us to discover information about the view when we do URL
            reverse lookups.  Used for breadcrumb generation.
            """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
                cls.queryset._fetch_all = force_evaluation
    
                view = super().as_view(**initkwargs)
                view.cls = cls
                view.initkwargs = initkwargs
    
                # Note: session based authentication is explicitly CSRF validated,
                # all other authentication is CSRF exempt.
                return csrf_exempt(view)
  5. 当客户端访问路由时,view变量对应的View.as_view方法调用,并且在调用时获取了view变量的名称空间中携带的自定义类其实例化对象的参数
    # django\views\generic\base.py
    @classonlymethod
    def as_view(cls, **initkwargs):
        """
            Main entry point for a request-response process.
            """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
         if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))
    
     def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs
    
        # take name and docstring from class
        update_wrapper(view, cls, updated=())
    
        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

    发现函数内调用了self.dispatch函数;

  6. 所以去self也就是自定义类中找dispatch方法,而自定义类中没有dispatch方法,于是就从父类APIView类中找,确实找到了;
  7. 因为APIView类继承View类,所以其实它是重写了dispatch方法;
  8. 重写的dispatch方法:
    1. 在执行请求逻辑前:请求模块(二次封装request)、解析模块(三种数据包格式的数据解析)
    2. 在执行请求逻辑中:进行三大认证,通过则正常响应;不通过就进入异常模块(执行出现任何异常就会交给异常模块处理)
    3. 在执行请求逻辑后:响应模块(二次封装response)、渲染模块(响应的数据能用JSON和HTML页面两种方式渲染)
    
    def dispatch(self, request, *args, **kwargs):
        """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
        self.args = args
        self.kwargs = kwargs
        # 二次封装request对象,包含解析模块
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    
        try:
            # 三大认证(认证、权限、频率),用来替换csrf安全认证,要比csrf认证强大得多
            self.initial(request, *args, **kwargs)
    
            # Get the appropriate handler method
            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
                response = handler(request, *args, **kwargs)
    
     except Exception as exc:
            # 异常模块 - 处理请求异常分支的
            response = self.handle_exception(exc)
            # 二次封装response,处理了结果渲染
            self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

2、请求模块

  1. 将wsgi的request对象二次封装转化成drf的request对象;
  2. 封装后的request对象完全兼容wsgi的request对象,并且将原request对象保存在新request._request中;
  3. 重新格式化数据。请求数据存放位置:
    • 拼接参数:request.query_params
    • 数据包参数:request.data
  4. 源码分析:
    # dispatch中会对request进行二次封装:
    # request = self.initialize_request(request, *args, **kwargs)
    # 调用了initialize_request()函数
    def initialize_request(self, request, *args, **kwargs):
        """
            Returns the initial request object.
            """
        parser_context = self.get_parser_context(request)
    
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    
    # 可见在函数中将wsgi的request对象用drf的Request类进行了二次封装
    
    class Request:
        # 部分代码
        def __init__(self, request, parsers=None, authenticators=None...)
    
            self._request = request
            self.parsers = parsers or ()
            self.authenticators = authenticators or ()
            self.negotiator = negotiator or self._default_negotiator()
            self.parser_context = parser_context
            self._data = Empty  # Empty为空类,仅仅用作替代,
            self._files = Empty  # 相当于None
            self._full_data = Empty
            self._content_type = Empty
            self._stream = Empty

3、解析模块

settings配置文件中注册:

REST_FRAMEWORK = {
    # 全局配置解析类:适用于所有视图类
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],
}

view视图文件中导入解析模块

from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
# 局部配置
class BookAPIView(APIView):
    # 局部配置解析类:只适用当前视图类
    parser_classes = [JSONParser, FormParser, MultiPartParser]

解析模块只处理数据包参数。

参数格式有三种:form-data,urlencoded,json

1、全局配置所有视图类的解析方式,解析配置可以配置三种
2、局部配置当前视图类的解析方式,解析配置可以配置三种
3、配置的查找顺序:局部(视图类)->全局(settings文件中的drf配置)->默认(drf的默认配置)

注:该模块了解,但全局局部配置是重点

4、源码分析

# dispatch对request进行二次封装中:
request = self.initialize_request(request, *args, **kwargs)

# initialize_request
parsers=self.get_parsers()

# get_parsers
return [renderer() for renderer in self.renderer_classes]

4、响应模块

views视图函数中使用响应模块:

from rest_framework.response import Response
class BookAPIView(APIView):
    def get(self, request, *args, **kwargs):
        response = Response(
            data={
                'msg': 'apiview get ok'
            },
            status=status.HTTP_404_NOT_FOUND,
        )
        return response

data:响应数据

status:响应的网络状态码

template_name:drf完成前后不分离返回页面,但是就不可以返回data(不需要了解)

headers:响应头,一般不规定,走默认

exception:一般异常响应会将其设置成True,默认为False(不设置也没事)

content_type:默认就是application/json,不需要处理。

5、渲染模块(了解)

settings配置文件中配置:

# drf框架自定义配置
REST_FRAMEWORK = {
    # 全局配置渲染类:适用于所有视图类
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        # 上线后尽量关闭
        # 'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}

postman请求结果是json,浏览器请求结果是页面。

可以局部与全局配置。

6、异常模块

settings配置文件中配置:

REST_FRAMEWORK = {
    # 异常模块:异常处理函数
    # drf处理
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    # 自定义处理
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}

然后在app应用文件夹下创建处理异常的py文件:

# exception.py
# 一定要在settings文件中将异常模块配置自己的异常处理函数
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)
    detail = '%s - %s - %s' % (context.get('view'), context.get('request').method, exc)
    if not response:  # 服务端错误
        response =  Response({'detail': detail})
    else:
        response.data = {'detail': detail}

    # 核心:要将response.data.get('detail')信息记录到日志文件
    # logger.waring(response.data.get('detail'))

    return response

self.initial(...)

原文地址:https://www.cnblogs.com/bowendown/p/12094232.html

时间: 2024-08-30 18:19:00

73-DRF的封装:APIView类及五大模块的相关文章

Directx11学习笔记【二】 将HelloWin封装成类

我们把上一个教程的代码封装到一个类中来方便以后的使用. 首先新建一个空工程叫做MyHelloWin,添加一个main.cpp文件,然后新建一个类叫做MyWindow,将于窗体有关的操作封装到里面 MyWindow.h文件 1 /************************************************************************ 2 Directx11学习笔记[2] 将HelloWin封装成类 3 2016.01 by zhangbaochong 4 /

【Cocos2d入门教程八】浅析Cocoss2d下的音频引擎及封装音频类

Cocos2d-x提供了一个音频CocosDenshion引擎,CocosDenshion引擎可以独立于Cocos2d-x单独使用,CocosDenshion引擎本质上封装了OpenAL音频处理库.具体使用的API是SimpleAudioEngine.在大多数平台上,cocos2d-x可以调用不同的SDK API来播放背景音乐和音效.CocosDenshion音频引擎只能在同一时间只能播放一首背景音乐,但是能同时播放许多音效.下面列举CocosDenshion引擎下的SimpleAudioEng

第四章:定义封装的类类型

就.NET平台而言,最基本的编程结构就是类类型. 类是由字段数据(通常叫做成员变量)以及操作这个数据的成员(构造函数,熟悉,方法,事件等)所构成的自定义类型. 注:类中的字段很少定义为公共(public)的,为了保护状态数据的完整性,最好将数据定义为私有(private)的或是受保护(protected)的. public class CDome { public int id; private int code; protected int pCode; public CDome(){} pu

二次封装Response类、views:视图 generics:工具视图 mixins:视图工具集 viewsets:视图集

## 二次封装Response类 Response({ 'status': 0, 'msg': 'ok', 'results': [], 'token': '' # 有这样的额外的key-value数据结果 },status=http_status,headers=headers,exception=True|False) APIResponse() => Response({'status': 0,'msg': 'ok'}) """ ``` from rest_fra

非专业码农 JAVA学习笔记 3 抽象、封装和类(1)

抽象.封装和类(1)待续 首先声明,今天接了太多个电话,两个小时看书被打断多次,缩减为一个小时.不管了,走马观花也要看完几个小节: (一)抽象与封装:抽象-抽取和研究实际性问题加以考察(抽象-尼玛抽出对象…) 封装:相关操作封存在命名空间.类等 (二)java的类: 1.系统定义的类(用于import中引入) 类名 作用 Java.lang 语言包,默认加载 Java.io, In out:文件数据流操作产生的输入输出流 Java.util 低级工具,如时间data类,变成数组vector类,h

jQuery Ajax封装通用类 (linjq)

jQuery Ajax封装通用类 (linjq) $(function(){ /** * ajax封装 * url 发送请求的地址 * data 发送到服务器的数据,数组存储,如:{"date": new Date().getTime(), "state": 1} * async 默认值: true.默认设置下,所有请求均为异步请求.如果需要发送同步请求,请将此选项设置为 false. * 注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行. * t

简易高重用的jdbcutils工具封装实现类以及简易连接池实现

由于现在发现做个小项目都是导入n多的依赖包,很烦琐,只想快点开发完一个个的小需求项目,这个时候真心不想用框架,只能自己写个jdbcutils,虽然网上有很多有apache的,阿里的,但是感觉用过后都不怎么顺手,然后自己花了点时间写个新的,不喜勿喷 1.我们要写个resultset集合转成bean的回调接口,这个用过spring jdbc的人都知道这玩意 package org.framework.mvc.jdbc.bean; import java.sql.ResultSet; import j

iOS开发—音频的播放的简单介绍和封装工具类

iOS开发—音频的播放的简单介绍和封装工具类 一.音效的播放简单介绍 简单来说,音频可以分为2种 (1)音效 又称“短音频”,通常在程序中的播放时长为1~2秒 在应用程序中起到点缀效果,提升整体用户体验 (2)音乐 比如游戏中的“背景音乐”,一般播放时间较长 框架:播放音频需要用到AVFoundation.framework框架 二.音效的播放 1.获得音效文件的路径 NSURL *url = [[NSBundle mainBundle] URLForResource:@"m_03.wav&qu

微信公众号开发系列-Http请求封装基类

HttpHelper请求封装基类,支持get请求和POS请求,方便微信开发接口交互,为后面接口交互做准备. 1.HttpHelper帮助基类 [csharp] view plaincopy using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Net; using System.Net.Security; namespa