drf简介

封装规范

'''
from rest_framework.views import APIView
from rest_framework.views import Request
from rest_framework.views 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
'''

drf配置

'''
# d_proj\d_proj\settings.py
# 注册
INSTALLED_APPS = [
    ...,

    'rest_framework',

    ...
]

# drf框架自定义配置
REST_FRAMEWORK = {
	...
}
'''

django的Jsonresponse: from django.http import JsonResponse, 只能返回字典类型的数据

drf的Response: from rest_framework.response import Response, 可以返回多种类型的数据

基于drf的请求生命周期

  1. APIView类继承View类, 在APIView类中重写了as_view和dispatch方法
  2. 重写的as_view方法, 主体还是View中的as_view方法, 只是在返回view函数地址时, 局部禁用了csrf认证
  3. 重写的dispatch方法
    1. 在执行逻辑前: 请求模块二次封装request, 解析模块(三种数据包格式的数据解析)
    2. 在执行逻辑时: 执行出现任何异常交给异常模块处理
    3. 在执行逻辑后: 响应模快二次封装response, 渲染模块(响应的数据能JSON和页面两种模式渲染)

dispatch

'''
# G:\python-3.6.4-64\Lib\site-packages\django\views\generic\base.py
class View(object):
    ...
    @classonlymethod
    def as_view(cls, **initkwargs):
        ...
        def view(request, *args, **kwargs):  # view只是一个普通函数, 既不是类的绑定方法, 也不是对象的绑定方法, 接收request, 无名分组参数, 有名分组参数
            self = cls(**initkwargs)  # 此时的cls为调用as_view方法的类, 即class BookAPIView, self为BookAPIView实例化出来的对象
            ...
            return self.dispatch(request, *args, **kwargs)  # BookAPIView实例化出来的对象调用dispatch方法, 将request, 无名分组参数, 有名分组参数传入dispatch
        ...
        return view


# G:\python-3.6.4-64\Lib\site-packages\rest_framework\views.py
class APIView(View):
    ...
    @classmethod
    def as_view(cls, **initkwargs):
        ...
        view = super().as_view(**initkwargs)  # 重用父类View的as_view方法
        ...
        return csrf_exempt(view)  # 局部禁用csrf认证


    def dispatch(self, request, *args, **kwargs):  # 该方法被BookAPIView实例化出来的对象调用, self参数即为该对象
        ...
        request = self.initialize_request(request, *args, **kwargs)  # 二次封装request对象
        self.request = request

        try:
            self.initial(request, *args, **kwargs)  # 三大认证(认证, 权限, 频率), 用来替换csrf认证
            ...
            response = handler(request, *args, **kwargs)
        except Exception as exc:
            response = self.handle_exception(exc)  # 异常模块, 处理请求异常分支

        self.response = self.finalize_response(request, response, *args, **kwargs)  # 二次封装response对象
        
        return self.response
'''

解析模块

只处理数据包参数: form-data, form-urlencoded, json

  • 局部配置当前视图类的解析方式

    '''
    # C:\...\d_proj\api\views.py
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
    
    
    class BookAPIView(APIView):
        parser_classes = [JSONParser, FormParser, MultiPartParser]
    
        ...
    '''
    
  • 全局配置所有视图类的解析方式

    # C:\...\d_proj\d_proj\settings.py
    # drf框架自定义配置
    REST_FRAMEWORK = {
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
            'rest_framework.parsers.FormParser',
            'rest_framework.parsers.MultiPartParser'
        ],
    }
    
  • 所有视图类的默认解析方式

    '''
    # G:\...\rest_framework\settings.py
    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    DEFAULTS = {  # drf的settings.py中的默认配置
        ...,
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
            'rest_framework.parsers.FormParser',
            'rest_framework.parsers.MultiPartParser'
        ],
        ...
    }
    '''
    

配置的查找顺序

局部(自定义的CBV中)-->全局(django项目的setting.py中)-->默认(drf的settings.py中)

'''
# G:\...\rest_framework\views.py
class APIView(View):
    ...
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES  # 优先从django项目的settings文件中查找解析模块, 然后从drf的settings文件中查找
    ...
    
    def dispatch(self, request, *args, **kwargs):  # dispatch方法被BookAPIView实例化出来的对象调用, self参数即为该对象
        ...
        request = self.initialize_request(request, *args, **kwargs)  # 重新初始化wsgi封装的request对象, 得到新的request
        ...
        
    def initialize_request(self, request, *args, **kwargs):
        ...
        parser_context = self.get_parser_context(request)  # 获取解析数据

        return Request(  # 在Request模块中通过解析模块处理wsgi封装的request对象
            request,
            parsers=self.get_parsers(),  # 获取解析对象
            ...
            parser_context=parser_context
        )

    def get_parser_context(self, http_request):
        ...
        return {  # 将解析数据拆解到字典中
            'view': self,
            'args': getattr(self, 'args', ()),
            'kwargs': getattr(self, 'kwargs', {})
        }

    def get_parsers(self):  # self为BookAPIView实例化出来的对象
        ...
        return [parser() for parser in self.parser_classes]  # 优先查找BookAPIView及其对象的名称空间
 
 
# G:\...\rest_framework\request.py
class Request:
    ...
    def __init__(self, request, parsers=None, ..., parser_context=None):  # 初始化Request类的自定义属性
        ...
        self._request = request
        ...
        self.parser_context = parser_context
        ...
        self._full_data = Empty
        ...
 
 
# G:\...\rest_framework\settings.py
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
DEFAULTS = {  # drf的settings.py中的默认配置
    ...,
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],
    ...
}
'''

响应模块及渲染模块

响应模块

data: 响应数据

status: 响应状态码

'''
    def post(self, request, *args, **kwargs):
        ...
        response = Response(
            data={
                'msg': 'api view post ok',
            },
            status=404
        )
        return response
'''

渲染模块

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

  • 局部配置

    '''
    # C:\...\d_proj\api\views.py
    from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
    
    
    class BookAPIView(APIView):
        renderer_classes = [BrowsableAPIRenderer, JSONRenderer]
        ...
    '''
    
  • 全局配置

    # C:\...\d_proj\d_proj\settings.py
    # drf框架自定义配置
    REST_FRAMEWORK = {
        ...
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],
    }
    
  • drf默认配置

    '''
    # G:\...\rest_framework\views.py
    class APIView(View):
        ...
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
        ...
        
    
    # DEFAULTS = {
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],
        ...
    }
    ...
    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    '''
    

请求模块

  1. 将wsgi的request对象转换成drf的Request类的对象
  2. 封装后的request对象完全兼容wsgi的request对象, 并且将原request对象保存在新request对象的_request属性中
  3. 重新格式化请求数据的存放位置
    • 拼接参数: request.query_params---@property执行方法属性, 给request._request.method重新命名
    • 数据包参数: request.data---@property执行方法属性, 值依赖于request._full_data
  4. request.method, 通过在Request类中自定义 __getattr__ 方法优先从request._request中查找
'''
# G:\...\rest_framework\views.py
class APIView(View):
        def dispatch(self, request, *args, **kwargs):
        ...
        request = self.initialize_request(request, *args, **kwargs)
        ...
        
    def initialize_request(self, request, *args, **kwargs):
        ...
        return Request(
            request,
            ...
        )


# G:\...\rest_framework\request.py
class Request:
    ...
    def __init__(self, request, ...):
        ...
        self._request = request
        ...
        self._full_data = Empty
        ...

    @property
    def query_params(self):
        ...
        return self._request.GET

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data  # 将三种类型的数据包参数解析到._full_data中
        
	...

    def __getattr__(self, attr):
        ...
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
'''

异常模块

  • 自定义异常处理配置: 1. 在api文件夹中新建exception.py文件; 2. 在项目的settings.py中进行相关配置

    '''
    C:\...\d_proj\api\exception.py
    from rest_framework.response import Response
    from rest_framework.views import exception_handler as drf_exception_handler
    
    
    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}, 500)  # 响应模块初始化实例时数据传入第一个默认形参data中
        else:
            response.data = {'detail': detail}  # 重写前端异常时的响应信息
    
        return response  # 核心: 后端在返回response前需要将异常信息记录到日志
        
    
    # C:\...\d_proj\d_proj\settings.py
    REST_FRAMEWORK = {
        ...,
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
    '''
    
'''
G:\...\rest_framework\views.py
class APIView(View):
	settings = api_settings  # settings指向配置文件
	
    def dispatch(self, request, *args, **kwargs):
        ...
        try:
            self.initial(request, *args, **kwargs)  # 三大认证校验

            if request.method.lower() in self.http_method_names:  # 前端请求方式判断
                handler = getattr(self, request.method.lower(),  # handler句柄指向对应的视图类中的对应方法
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed  # handler句柄指向前端抛错函数

            response = handler(request, *args, **kwargs)  # 执行handler句柄指向的函数, 并将结果封装到response总

        except Exception as exc:  # exc能捕获的异常信息来源于三大认证过程以及执行handler句柄指向的函数的过程
            response = self.handle_exception(exc)  # 处理异常信息并将处理结果封装到response中
        ...
        
    def http_method_not_allowed(self, request, *args, **kwargs):  # 对应前端请求方式错误
        ...
        raise exceptions.MethodNotAllowed(request.method)
        
    def handle_exception(self, exc):  # 处理多种类型的异常
        ...
        exception_handler = self.get_exception_handler()  # 获取处理异常的方法

        context = self.get_exception_handler_context()  # 获取异常处理的额外参数
        response = exception_handler(exc, context)

        if response is None:
            self.raise_uncaught_exception(exc)

        response.exception = True
        return response

    def get_exception_handler(self):
        return self.settings.EXCEPTION_HANDLER  # 从配置文件中导入异常处理的相关数据
        
    def get_exception_handler_context(self):
        ...
        return {
            'view': self,  # 处理当前前端请求的类的实例化对象
            'args': getattr(self, 'args', ()),  # 匹配url得到的无名分组
            'kwargs': getattr(self, 'kwargs', {}),  # 匹配url得到的有名分组
            'request': getattr(self, 'request', None)  # 二次封装后的request
        }


def exception_handler(exc, context):
    ...  # 处理前端异常
        return Response(data, status=exc.status_code, headers=headers)

    return None  # 后端异常返回None


# G:\...\rest_framework\settings.py
DEFAULTS = {  
    ...,
    # Exception handling
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',  # drf框架中异常模块的默认配置
    ...,
}
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
'''