DRF之Response源码分析
DRF之Response源码分析
【一】响应类的对象Response源码
【1】路由
from django.contrib import admin
from django.urls import path
from book import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.TestView.as_view()),
]
【2】视图
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
def get(self, request, *args, **kwargs):
return Response('ok')
【3】源码
from rest_framework.response import Response
class Response(SimpleTemplateResponse):
"""
An HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
super().__init__(None, status=status)
if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
self.data = data
self.template_name = template_name
self.exception = exception
self.content_type = content_type
if headers:
for name, value in headers.items():
self[name] = value
@property
def rendered_content(self):
renderer = getattr(self, 'accepted_renderer', None)
accepted_media_type = getattr(self, 'accepted_media_type', None)
context = getattr(self, 'renderer_context', None)
assert renderer, ".accepted_renderer not set on Response"
assert accepted_media_type, ".accepted_media_type not set on Response"
assert context is not None, ".renderer_context not set on Response"
context['response'] = self
media_type = renderer.media_type
charset = renderer.charset
content_type = self.content_type
if content_type is None and charset is not None:
content_type = "{}; charset={}".format(media_type, charset)
elif content_type is None:
content_type = media_type
self['Content-Type'] = content_type
ret = renderer.render(self.data, accepted_media_type, context)
if isinstance(ret, str):
assert charset, (
'renderer returned unicode, and did not specify '
'a charset value.'
)
return ret.encode(charset)
if not ret:
del self['Content-Type']
return ret
@property
def status_text(self):
"""
Returns reason text corresponding to our HTTP response status code.
Provided for convenience.
"""
return responses.get(self.status_code, '')
def __getstate__(self):
"""
Remove attributes from the response that shouldn't be cached.
"""
state = super().__getstate__()
for key in (
'accepted_renderer', 'renderer_context', 'resolver_match',
'client', 'request', 'json', 'wsgi_request'
):
if key in state:
del state[key]
state['_closable_objects'] = []
return state
【二】源码分析
【1】__init__
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
# 稍微更改init参数。
Alters the init arguments slightly.
# 例如,删除“template_name”,改为使用“data”。
For example, drop 'template_name', and instead use 'data'.
# 设置“renderer”和“media_type”
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
# 调用父类的 init 方法
super().__init__(None, status=status)
# 判断当前处理过的数据是否是 序列化过后的数据
if isinstance(data, Serializer):
# 这里的意思是 ,data 只能是序列化后的 serializer.data 或者是 serializer.errors
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
# 初始化 data 数据
self.data = data
# 初识化模板名字
self.template_name = template_name
# 初始化异常捕获对象
self.exception = exception
# 初始化当前数据类型
self.content_type = content_type
# 如果头部有数据
if headers:
# 遍历头部数据
for name, value in headers.items():
# 添加到响应数据中
self[name] = value
【2】rendered_content
# 包装成数据属性
@property
# 渲染响应内容
def rendered_content(self):
# 获取响应对象中的属性值,获取模版渲染器
renderer = getattr(self, 'accepted_renderer', None)
# 获取响应对象中的属性值,获取媒体类型
accepted_media_type = getattr(self, 'accepted_media_type', None)
# 获取响应对象中的属性值,获取处理过后的数据
context = getattr(self, 'renderer_context', None)
# 使用断言语句,确保上述获取到的属性值都不为空。如果为空,抛出异常并给出相应的错误提示
assert renderer, ".accepted_renderer not set on Response"
assert accepted_media_type, ".accepted_media_type not set on Response"
assert context is not None, ".renderer_context not set on Response"
# 设置上下文字典中的键response的值为当前的响应对象
context['response'] = self
# 获取渲染器对象的媒体类型
media_type = renderer.media_type
# 获取渲染器对象的编码集
charset = renderer.charset
# 获取渲染器对象的数据类型
content_type = self.content_type
# 如果content_type为空且charset不为空
if content_type is None and charset is not None:
# 将media_type和charset拼接为新的content_type值
content_type = "{}; charset={}".format(media_type, charset)
# 如果content_type还是为空
elif content_type is None:
# 则将其值设置为media_type
content_type = media_type
# 将响应的数据格式添加到响应对象头中
self['Content-Type'] = content_type
# 使用渲染器渲染数据
ret = renderer.render(self.data, accepted_media_type, context)
# 判断渲染出来的数据是不是字符串格式
if isinstance(ret, str):
# 若不存在,则抛出异常并提示渲染器返回了Unicode字符串但未指定字符集
assert charset, (
'renderer returned unicode, and did not specify '
'a charset value.'
)
# 如果存在,则将ret按指定的charset进行编码并返回
return ret.encode(charset)
# 如果ret是字符串类型,再次检查charset是否存在。
# 如果ret为空,则删除响应对象的Content-Type头部属性
if not ret:
del self['Content-Type']
# 返回渲染后的结果给调用方
return ret
【3】status_text
@property
# 状态码校验
def status_text(self):
"""
#返回与我们的HTTP响应状态代码相对应的原因文本。
Returns reason text corresponding to our HTTP response status code.
Provided for convenience.
"""
# 获取到响应对象中的状态码并返回
return responses.get(self.status_code, '')
def __getstate__(self):
"""
Remove attributes from the response that shouldn't be cached.
"""
# 调用了父类的__getstate__方法来获取父类中定义的状态,并将其赋值给变量state
state = super().__getstate__()
# 遍历 键
for key in (
'accepted_renderer', 'renderer_context', 'resolver_match',
'client', 'request', 'json', 'wsgi_request'
):
# 判断当前键是否在状态码对象中
# 如果存在则删除响应的响应头
if key in state:
del state[key]
# 将一个空列表赋值给state['_closable_objects']。
# 目的是确保对象在进行序列化时没有包含不必要的属性,从而避免潜在的问题。
state['_closable_objects'] = []
return state
【三】SimpleTemplateResponse(没什么价值)
class SimpleTemplateResponse(HttpResponse):
rendering_attrs = ['template_name', 'context_data', '_post_render_callbacks']
def __init__(self, template, context=None, content_type=None, status=None,
charset=None, using=None, headers=None):
# 很明显,将这两个成员称为“模板”
# It would seem obvious to call these next two members 'template' and
# “context”,但这些名称是作为测试客户端的一部分保留的
# 'context', but those names are reserved as part of the test Client
# API为了避免名称冲突,我们使用不同的名称。
# API. To avoid the name collision, we use different names.
# 初始化模版名字
self.template_name = template
# 初始化上下文对象
self.context_data = context
# 判断是够被使用
self.using = using
# 是否有回调函数
self._post_render_callbacks = []
# request将当前请求对象存储在已知的子类中
#关于请求,如TemplateResponse。它是在基类中定义的
#以最大限度地减少代码重复。
#这叫做自我_请求,因为self.request被覆盖
#django.test.client.client.与template_name和context_data不同,
#_request不应被视为公共API的一部分。
# _request stores the current request object in subclasses that know
# about requests, like TemplateResponse. It's defined in the base class
# to minimize code duplication.
# It's called self._request because self.request gets overwritten by
# django.test.client.Client. Unlike template_name and context_data,
# _request should not be considered part of the public API.
# 初始化 request 对象 ---- 上一步处理过的request对象(视图类中的request对象)
self._request = None
#content参数在这里没有意义,因为它将被替换
#使用渲染的模板,所以我们总是传递空字符串,以便
#防止错误并提供更短的签名。
# content argument doesn't make sense here because it will be replaced
# with rendered template so we always pass empty string in order to
# prevent errors and provide shorter signature.
# 调用父类初识化方法初始化
super().__init__('', content_type, status, charset=charset, headers=headers)
#_is_render跟踪模板和上下文是否已烘焙
#转化为最终响应。
#超级__init__不知道什么比将self.content设置为更好的了
#我们刚刚给它的空字符串,它错误地设置了_is_render
#True,所以我们在调用super__init__之后将其初始化为False。
# _is_rendered tracks whether the template and context has been baked
# into a final response.
# Super __init__ doesn't know any better than to set self.content to
# the empty string we just gave it, which wrongly sets _is_rendered
# True, so we initialize it to False after the call to super __init__.
self._is_rendered = False
def __getstate__(self):
"""
Raise an exception if trying to pickle an unrendered response. Pickle
only rendered data, not the data used to construct the response.
"""
obj_dict = self.__dict__.copy()
if not self._is_rendered:
raise ContentNotRenderedError('The response content must be '
'rendered before it can be pickled.')
for attr in self.rendering_attrs:
if attr in obj_dict:
del obj_dict[attr]
return obj_dict
def resolve_template(self, template):
"""Accept a template object, path-to-template, or list of paths."""
if isinstance(template, (list, tuple)):
return select_template(template, using=self.using)
elif isinstance(template, str):
return get_template(template, using=self.using)
else:
return template
def resolve_context(self, context):
return context
@property
def rendered_content(self):
"""Return the freshly rendered content for the template and context
described by the TemplateResponse.
This *does not* set the final content of the response. To set the
response content, you must either call render(), or set the
content explicitly using the value of this property.
"""
template = self.resolve_template(self.template_name)
context = self.resolve_context(self.context_data)
return template.render(context, self._request)
def add_post_render_callback(self, callback):
"""Add a new post-rendering callback.
If the response has already been rendered,
invoke the callback immediately.
"""
if self._is_rendered:
callback(self)
else:
self._post_render_callbacks.append(callback)
def render(self):
"""Render (thereby finalizing) the content of the response.
If the content has already been rendered, this is a no-op.
Return the baked response instance.
"""
retval = self
if not self._is_rendered:
self.content = self.rendered_content
for post_callback in self._post_render_callbacks:
newretval = post_callback(retval)
if newretval is not None:
retval = newretval
return retval
@property
def is_rendered(self):
return self._is_rendered
def __iter__(self):
if not self._is_rendered:
raise ContentNotRenderedError(
'The response content must be rendered before it can be iterated over.'
)
return super().__iter__()
@property
def content(self):
if not self._is_rendered:
raise ContentNotRenderedError(
'The response content must be rendered before it can be accessed.'
)
return super().content
@content.setter
def content(self, value):
"""Set the content for the response."""
HttpResponse.content.fset(self, value)
self._is_rendered = True
class TemplateResponse(SimpleTemplateResponse):
rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_request']
def __init__(self, request, template, context=None, content_type=None,
status=None, charset=None, using=None, headers=None):
super().__init__(template, context, content_type, status, charset, using, headers=headers)
self._request = request
【四】响应类的对象Respons参数详解
【1】data
- 响应体的内容,可以字符串,字典,列表
【2】status
http响应状态码
- drf把所有响应码都定义成了一个常量
from rest_framework.status import HTTP_200_OK HTTP_100_CONTINUE = 100
HTTP_101_SWITCHING_PROTOCOLS = 101
HTTP_102_PROCESSING = 102
HTTP_103_EARLY_HINTS = 103
HTTP_200_OK = 200
HTTP_201_CREATED = 201
HTTP_202_ACCEPTED = 202
HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
HTTP_204_NO_CONTENT = 204
HTTP_205_RESET_CONTENT = 205
HTTP_206_PARTIAL_CONTENT = 206
HTTP_207_MULTI_STATUS = 207
HTTP_208_ALREADY_REPORTED = 208
HTTP_226_IM_USED = 226
HTTP_300_MULTIPLE_CHOICES = 300
HTTP_301_MOVED_PERMANENTLY = 301
HTTP_302_FOUND = 302
HTTP_303_SEE_OTHER = 303
HTTP_304_NOT_MODIFIED = 304
HTTP_305_USE_PROXY = 305
HTTP_306_RESERVED = 306
HTTP_307_TEMPORARY_REDIRECT = 307
HTTP_308_PERMANENT_REDIRECT = 308
HTTP_400_BAD_REQUEST = 400
HTTP_401_UNAUTHORIZED = 401
HTTP_402_PAYMENT_REQUIRED = 402
HTTP_403_FORBIDDEN = 403
HTTP_404_NOT_FOUND = 404
HTTP_405_METHOD_NOT_ALLOWED = 405
HTTP_406_NOT_ACCEPTABLE = 406
HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
HTTP_408_REQUEST_TIMEOUT = 408
HTTP_409_CONFLICT = 409
HTTP_410_GONE = 410
HTTP_411_LENGTH_REQUIRED = 411
HTTP_412_PRECONDITION_FAILED = 412
HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_414_REQUEST_URI_TOO_LONG = 414
HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
HTTP_417_EXPECTATION_FAILED = 417
HTTP_418_IM_A_TEAPOT = 418
HTTP_421_MISDIRECTED_REQUEST = 421
HTTP_422_UNPROCESSABLE_ENTITY = 422
HTTP_423_LOCKED = 423
HTTP_424_FAILED_DEPENDENCY = 424
HTTP_425_TOO_EARLY = 425
HTTP_426_UPGRADE_REQUIRED = 426
HTTP_428_PRECONDITION_REQUIRED = 428
HTTP_429_TOO_MANY_REQUESTS = 429
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451
HTTP_500_INTERNAL_SERVER_ERROR = 500
HTTP_501_NOT_IMPLEMENTED = 501
HTTP_502_BAD_GATEWAY = 502
HTTP_503_SERVICE_UNAVAILABLE = 503
HTTP_504_GATEWAY_TIMEOUT = 504
HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_506_VARIANT_ALSO_NEGOTIATES = 506
HTTP_507_INSUFFICIENT_STORAGE = 507
HTTP_508_LOOP_DETECTED = 508
HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509
HTTP_510_NOT_EXTENDED = 510
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511
【3】template_name
- 模板名字,用浏览器访问,看到好看的页面,用postman访问,返回正常数据
- 自定制页面
- 根本不用
【4】headers
- 响应头加数据(后面讲跨域问题再讲)
- headers=
【5】content_type
- 响应编码,一般不用
三个重要的参数:data,status,headers
【五】请求响应的格式
- 默认是两种:
- 纯json
- 浏览器看到的样子
【1】限制方式一:
- 在视图类上写
- 只是局部视图类有效
# 总共有两个个:JSONRenderer,BrowsableAPIRenderer
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookView(APIView):
renderer_classes = [JSONRenderer]
【2】限制方式二:
- 在配置文件中写
- 全局有效
# drf的配置,统一写成它
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
# 'rest_framework.renderers.BrowsableAPIRenderer',
],
}
【3】全局配置了只支持json,局部想支持2个
- 只需要在局部,视图类中,写2个即可
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookView(APIView):
renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
DRF之Response源码分析的更多相关文章
- Django(48)drf请求模块源码分析
前言 APIView中的dispatch是整个请求生命过程的核心方法,包含了请求模块,权限验证,异常模块和响应模块,我们先来介绍请求模块 请求模块:request对象 源码入口 APIView类中di ...
- Django(49)drf解析模块源码分析
前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...
- Django(50)drf异常模块源码分析
异常模块源码入口 APIView类中dispatch方法中的:response = self.handle_exception(exc) 源码分析 我们点击handle_exception跳转,查看该 ...
- Django(51)drf渲染模块源码分析
前言 渲染模块的原理和解析模块是一样,drf默认的渲染有2种方式,一种是json格式,另一种是模板方式. 渲染模块源码入口 入口:APIView类中dispatch方法中的:self.response ...
- Djang drf:APIView源码分析
Django REST framework 简介 在序列化与反序列化时,虽然操作的数据不尽相同,但是执行的过程却是相似的,也就是说这部分代码是可以复用简化编写的. 开发REST AP ...
- 18.drf request及源码分析
REST framework的 Request 类扩展了Django标准的 HttpRequest ,添加了对REST framework请求解析和身份验证的支持. 源代码片段: class Requ ...
- Django框架之DRF 认证组件源码分析、权限组件源码分析、频率组件源码分析
认证组件 权限组件 频率组件
- drf-day5——反序列化类校验部分源码分析、断言、drf请求、drf响应、视图组件及两个视图基类、基于GenericAPIView+5个视图扩展类
目录 一.反序列化类校验部分源码解析(了解) 二.断言 三.drf之请求 3.1 Request能够解析的前端传入的编码格式 3.2 Request类有哪些属性和方法(学过) 常用参数 Respons ...
- Django框架之drf:5、反序列化器校验部分源码分析、断言、drf之请求与响应、视图组件介绍及两个视图基类、代码部分实战
Django框架之drf 目录 Django框架之drf 一.反序列化类校验部分源码解析 二.断言 三.drf之请求 1.Request能够解析的前端传入编码格式 2.Request类中的属性和方法 ...
- DRF的认证组件(源码分析)
DRF认证组件(源码分析) 1. 数据库建立用户表 在drf中也给我们提供了 认证组件 ,帮助我们快速实现认证相关的功能,例如: # models.py from django.db import m ...
随机推荐
- Markdown 中如何添加图片
语法  alt text:可选,图片标签,用来描述的关键词,图片加载错误时候的替代文本,也可用于SEO. uri: ...
- Visual Studio 中的 .sln 和 .suo 文件
解决方案文件1 Visual Studio 采用两种文件类型 .sln & .suo 来存储特定于解决方案的设置.这些文件总称为解决方案文件,为解决方案资源管理器提供显示管理文件的图形接口所需 ...
- 远程连接到轻量应用服务器PG数据库
不建议这样做,但是开发时方便需要.进入正题. PG是不支持远程连接的,需要连接直接该参数. 在其data目录里,有二个配置文件: pg_hba.conf:配置数据库的访问权限 postgresql.c ...
- 从源码解析 QGraphicsItem 旋转、缩放、平移、transform等变换操作,利用QGraphicsTransform实现变形动画
QGraphicsItem 有3种方式进行变换:1. 最简单方便的是使用 setRotation() .setScale():2. 使用 setTransform() 进行复杂变换:3. 还可以使用 ...
- 让 LLM 来评判 | 技巧与提示
这是 让 LLM 来评判 系列文章的第六篇,敬请关注系列文章: 基础概念 选择 LLM 评估模型 设计你自己的评估 prompt 评估你的评估结果 奖励模型相关内容 技巧与提示 LLM 评估模型已知偏 ...
- emmy断点调试
package.cpath = package.cpath .. ';C:/Users/Administrator/AppData/Roaming/JetBrains/IntelliJIdea2021 ...
- Navicat Premium 16激活教程(NavicatCracker)
1.安装Navicat Premium 16 (注意版本,这里以此版本为例):并下载激活工具 1.1.Navicat Premium 下载路径: http://www.navicat.com.cn/d ...
- 基于华为交换机的三层Clos架构(Leaf-Spine)配置指南
基于华为交换机的三层Clos架构(Leaf-Spine)配置指南 目录 设计原则 配置步骤 物理连接与基础配置 路由协议配置 VXLAN+EVPN配置 M-LAG高可用配置 BFD快速检测 验证命令 ...
- java.security.provider.getservice blocked
JDK版本: JDK8u192 bug: https://bugs.openjdk.org/browse/JDK-8206333 堆栈: "Common-Business-Thread-57 ...
- Asp.net core 少走弯路系列教程(五)HTTP 协议学习
前言 新人学习成本很高,网络上太多的名词和框架,全部学习会浪费大量的时间和精力. 新手缺乏学习内容的辨别能力,本系列文章为新手过滤掉不适合的学习内容(比如多线程等等),让新手少走弯路直通罗马. 作者认 ...