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`. |
| """ |
| |
| 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 |
【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" |
| |
| |
| 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 |
【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. |
| """ |
| |
| |
| 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 |
【三】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): |
| |
| |
| |
| |
| |
| |
| |
| |
| self.template_name = template |
| |
| self.context_data = context |
| |
| |
| self.using = using |
| |
| |
| self._post_render_callbacks = [] |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| self._request = None |
| |
| |
| |
| |
| |
| |
| |
| |
| super().__init__('', content_type, status, charset=charset, headers=headers) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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
【3】template_name
- 模板名字,用浏览器访问,看到好看的页面,用postman访问,返回正常数据
【5】content_type
三个重要的参数:data,status,headers
【五】请求响应的格式
【1】限制方式一:
| |
| from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer |
| class BookView(APIView): |
| renderer_classes = [JSONRenderer] |
【2】限制方式二:
| |
| REST_FRAMEWORK = { |
| 'DEFAULT_RENDERER_CLASSES': [ |
| 'rest_framework.renderers.JSONRenderer', |
| |
| ], |
| } |
【3】全局配置了只支持json,局部想支持2个
| from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer |
| class BookView(APIView): |
| renderer_classes = [JSONRenderer,BrowsableAPIRenderer] |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通