一、内置渲染器

REST框架包括许多内置的Renderer类,它们允许你使用各种媒体类型返回响应。还支持定义你自己的自定义渲染器。

  • 内置渲染器的使用

1、全局设置

可以使用DEFAULT_RENDERER_CLASSES设置全局默认的渲染器集。例如,以下设置将使用JSON作为主要媒体类型:

REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
}

2、局部设置

from rest_framework.pagination import PageNumberPagination
from rest_framework.parsers import JSONParser
from rest_framework.renderers import JSONRenderer class BookView(GenericViewSet):
"""
该视图只接受JSON数据的post请求
"""
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
parser_classes = [JSONParser,] """
返回JSON数据格式
"""
renderer_classes = [JSONRenderer,] def list(self,request): queryset= self.get_queryset() pg = PageNumberPagination()
paginate_queryset = pg.paginate_queryset(queryset=queryset,request=request,view=self)
#序列化分页数据
bs=self.get_serializer(paginate_queryset,many=True)
# return Response(bs.data)
return pg.get_paginated_response(bs.data)
  • 内置渲染器API种类

1、JSONRenderer

返回的就只是单纯的JSON类型的数据格式

class JSONRenderer(BaseRenderer):
"""
Renderer which serializes to JSON.
"""
media_type = 'application/json'
format = 'json'
encoder_class = encoders.JSONEncoder
ensure_ascii = not api_settings.UNICODE_JSON
compact = api_settings.COMPACT_JSON
strict = api_settings.STRICT_JSON # We don't set a charset because JSON is a binary encoding,
# that can be encoded as utf-8, utf-16 or utf-32.
# See: https://www.ietf.org/rfc/rfc4627.txt
# Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/
charset = None def get_indent(self, accepted_media_type, renderer_context):
if accepted_media_type:
# If the media type looks like 'application/json; indent=4',
# then pretty print the result.
# Note that we coerce `indent=0` into `indent=None`.
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
try:
return zero_as_none(max(min(int(params['indent']), 8), 0))
except (KeyError, ValueError, TypeError):
pass # If 'indent' is provided in the context, then pretty print the result.
# E.g. If we're being called by the BrowsableAPIRenderer.
return renderer_context.get('indent', None) def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render `data` into JSON, returning a bytestring.
"""
if data is None:
return bytes() renderer_context = renderer_context or {}
indent = self.get_indent(accepted_media_type, renderer_context) if indent is None:
separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS
else:
separators = INDENT_SEPARATORS ret = json.dumps(
data, cls=self.encoder_class,
indent=indent, ensure_ascii=self.ensure_ascii,
allow_nan=not self.strict, separators=separators
) # On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
# but if ensure_ascii=False, the return type is underspecified,
# and may (or may not) be unicode.
# On python 3.x json.dumps() returns unicode strings.
if isinstance(ret, six.text_type):
# We always fully escape \u2028 and \u2029 to ensure we output JSON
# that is a strict javascript subset. If bytes were returned
# by json.dumps() then we don't have these characters in any case.
# See: http://timelessrepo.com/json-isnt-a-javascript-subset
ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
return bytes(ret.encode('utf-8'))
return ret

JSONRenderer

2、TemplateHTMLRenderer

使用 Django 的标准模板将数据呈现为 HTML。与其他渲染器不同,传递给 Response 的数据不需要序列化。

class TemplateHTMLRenderer(BaseRenderer):
"""
An HTML renderer for use with templates. The data supplied to the Response object should be a dictionary that will
be used as context for the template. The template name is determined by (in order of preference): 1. An explicit `.template_name` attribute set on the response.
2. An explicit `.template_name` attribute set on this class.
3. The return result of calling `view.get_template_names()`. For example:
data = {'users': User.objects.all()}
return Response(data, template_name='users.html') For pre-rendered HTML, see StaticHTMLRenderer.
"""
media_type = 'text/html'
format = 'html'
template_name = None
exception_template_names = [
'%(status_code)s.html',
'api_exception.html'
]
charset = 'utf-8' def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders data to HTML, using Django's standard template rendering. The template name is determined by (in order of preference): 1. An explicit .template_name set on the response.
2. An explicit .template_name set on this class.
3. The return result of calling view.get_template_names().
"""
renderer_context = renderer_context or {}
view = renderer_context['view']
request = renderer_context['request']
response = renderer_context['response'] if response.exception:
template = self.get_exception_template(response)
else:
template_names = self.get_template_names(response, view)
template = self.resolve_template(template_names) if hasattr(self, 'resolve_context'):
# Fallback for older versions.
context = self.resolve_context(data, request, response)
else:
context = self.get_template_context(data, renderer_context)
return template.render(context, request=request) def resolve_template(self, template_names):
return loader.select_template(template_names) def get_template_context(self, data, renderer_context):
response = renderer_context['response']
if response.exception:
data['status_code'] = response.status_code
return data def get_template_names(self, response, view):
if response.template_name:
return [response.template_name]
elif self.template_name:
return [self.template_name]
elif hasattr(view, 'get_template_names'):
return view.get_template_names()
elif hasattr(view, 'template_name'):
return [view.template_name]
raise ImproperlyConfigured(
'Returned a template response with no `template_name` attribute set on either the view or response'
) def get_exception_template(self, response):
template_names = [name % {'status_code': response.status_code}
for name in self.exception_template_names] try:
# Try to find an appropriate error template
return self.resolve_template(template_names)
except Exception:
# Fall back to using eg '404 Not Found'
body = '%d %s' % (response.status_code, response.status_text.title())
template = engines['django'].from_string(body)
return template

TemplateHTMLRenderer

class UserDetail(generics.RetrieveAPIView):
"""
A view that returns a templated HTML representation of a given user.
"""
queryset = User.objects.all()
renderer_classes = (TemplateHTMLRenderer,) def get(self, request, *args, **kwargs):
self.object = self.get_object()
return Response({'user': self.object}, template_name='user_detail.html')

实例

详情参考:https://www.django-rest-framework.org/api-guide/renderers/#templatehtmlrenderer

3、BrowsableAPIRenderer

将数据呈现为可浏览的 HTML API

class BrowsableAPIRenderer(BaseRenderer):
"""
HTML renderer used to self-document the API.
"""
media_type = 'text/html'
format = 'api'
template = 'rest_framework/api.html'
filter_template = 'rest_framework/filters/base.html'
code_style = 'emacs'
charset = 'utf-8'
form_renderer_class = HTMLFormRenderer def get_default_renderer(self, view):
"""
Return an instance of the first valid renderer.
(Don't use another documenting renderer.)
"""
renderers = [renderer for renderer in view.renderer_classes
if not issubclass(renderer, BrowsableAPIRenderer)]
non_template_renderers = [renderer for renderer in renderers
if not hasattr(renderer, 'get_template_names')] if not renderers:
return None
elif non_template_renderers:
return non_template_renderers[0]()
return renderers[0]() def get_content(self, renderer, data,
accepted_media_type, renderer_context):
"""
Get the content as if it had been rendered by the default
non-documenting renderer.
"""
if not renderer:
return '[No renderers were found]' renderer_context['indent'] = 4
content = renderer.render(data, accepted_media_type, renderer_context) render_style = getattr(renderer, 'render_style', 'text')
assert render_style in ['text', 'binary'], 'Expected .render_style ' \
'"text" or "binary", but got "%s"' % render_style
if render_style == 'binary':
return '[%d bytes of binary content]' % len(content) return content def show_form_for_method(self, view, method, request, obj):
"""
Returns True if a form should be shown for this method.
"""
if method not in view.allowed_methods:
return # Not a valid method try:
view.check_permissions(request)
if obj is not None:
view.check_object_permissions(request, obj)
except exceptions.APIException:
return False # Doesn't have permissions
return True def _get_serializer(self, serializer_class, view_instance, request, *args, **kwargs):
kwargs['context'] = {
'request': request,
'format': self.format,
'view': view_instance
}
return serializer_class(*args, **kwargs) def get_rendered_html_form(self, data, view, method, request):
"""
Return a string representing a rendered HTML form, possibly bound to
either the input or output data. In the absence of the View having an associated form then return None.
"""
# See issue #2089 for refactoring this.
serializer = getattr(data, 'serializer', None)
if serializer and not getattr(serializer, 'many', False):
instance = getattr(serializer, 'instance', None)
if isinstance(instance, Page):
instance = None
else:
instance = None # If this is valid serializer data, and the form is for the same
# HTTP method as was used in the request then use the existing
# serializer instance, rather than dynamically creating a new one.
if request.method == method and serializer is not None:
try:
kwargs = {'data': request.data}
except ParseError:
kwargs = {}
existing_serializer = serializer
else:
kwargs = {}
existing_serializer = None with override_method(view, request, method) as request:
if not self.show_form_for_method(view, method, request, instance):
return if method in ('DELETE', 'OPTIONS'):
return True # Don't actually need to return a form has_serializer = getattr(view, 'get_serializer', None)
has_serializer_class = getattr(view, 'serializer_class', None) if (
(not has_serializer and not has_serializer_class) or
not any(is_form_media_type(parser.media_type) for parser in view.parser_classes)
):
return if existing_serializer is not None:
try:
return self.render_form_for_serializer(existing_serializer)
except TypeError:
pass if has_serializer:
if method in ('PUT', 'PATCH'):
serializer = view.get_serializer(instance=instance, **kwargs)
else:
serializer = view.get_serializer(**kwargs)
else:
# at this point we must have a serializer_class
if method in ('PUT', 'PATCH'):
serializer = self._get_serializer(view.serializer_class, view,
request, instance=instance, **kwargs)
else:
serializer = self._get_serializer(view.serializer_class, view,
request, **kwargs) return self.render_form_for_serializer(serializer) def render_form_for_serializer(self, serializer):
if hasattr(serializer, 'initial_data'):
serializer.is_valid() form_renderer = self.form_renderer_class()
return form_renderer.render(
serializer.data,
self.accepted_media_type,
{'style': {'template_pack': 'rest_framework/horizontal'}}
) def get_raw_data_form(self, data, view, method, request):
"""
Returns a form that allows for arbitrary content types to be tunneled
via standard HTML forms.
(Which are typically application/x-www-form-urlencoded)
"""
# See issue #2089 for refactoring this.
serializer = getattr(data, 'serializer', None)
if serializer and not getattr(serializer, 'many', False):
instance = getattr(serializer, 'instance', None)
if isinstance(instance, Page):
instance = None
else:
instance = None with override_method(view, request, method) as request:
# Check permissions
if not self.show_form_for_method(view, method, request, instance):
return # If possible, serialize the initial content for the generic form
default_parser = view.parser_classes[0]
renderer_class = getattr(default_parser, 'renderer_class', None)
if hasattr(view, 'get_serializer') and renderer_class:
# View has a serializer defined and parser class has a
# corresponding renderer that can be used to render the data. if method in ('PUT', 'PATCH'):
serializer = view.get_serializer(instance=instance)
else:
serializer = view.get_serializer() # Render the raw data content
renderer = renderer_class()
accepted = self.accepted_media_type
context = self.renderer_context.copy()
context['indent'] = 4 # strip HiddenField from output
data = serializer.data.copy()
for name, field in serializer.fields.items():
if isinstance(field, serializers.HiddenField):
data.pop(name, None)
content = renderer.render(data, accepted, context)
# Renders returns bytes, but CharField expects a str.
content = content.decode('utf-8')
else:
content = None # Generate a generic form that includes a content type field,
# and a content field.
media_types = [parser.media_type for parser in view.parser_classes]
choices = [(media_type, media_type) for media_type in media_types]
initial = media_types[0] class GenericContentForm(forms.Form):
_content_type = forms.ChoiceField(
label='Media type',
choices=choices,
initial=initial,
widget=forms.Select(attrs={'data-override': 'content-type'})
)
_content = forms.CharField(
label='Content',
widget=forms.Textarea(attrs={'data-override': 'content'}),
initial=content,
required=False
) return GenericContentForm() def get_name(self, view):
return view.get_view_name() def get_description(self, view, status_code):
if status_code in (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN):
return ''
return view.get_view_description(html=True) def get_breadcrumbs(self, request):
return get_breadcrumbs(request.path, request) def get_extra_actions(self, view):
if hasattr(view, 'get_extra_action_url_map'):
return view.get_extra_action_url_map()
return None def get_filter_form(self, data, view, request):
if not hasattr(view, 'get_queryset') or not hasattr(view, 'filter_backends'):
return # Infer if this is a list view or not.
paginator = getattr(view, 'paginator', None)
if isinstance(data, list):
pass
elif paginator is not None and data is not None:
try:
paginator.get_results(data)
except (TypeError, KeyError):
return
elif not isinstance(data, list):
return queryset = view.get_queryset()
elements = []
for backend in view.filter_backends:
if hasattr(backend, 'to_html'):
html = backend().to_html(request, queryset, view)
if html:
elements.append(html) if not elements:
return template = loader.get_template(self.filter_template)
context = {'elements': elements}
return template.render(context) def get_context(self, data, accepted_media_type, renderer_context):
"""
Returns the context used to render.
"""
view = renderer_context['view']
request = renderer_context['request']
response = renderer_context['response'] renderer = self.get_default_renderer(view) raw_data_post_form = self.get_raw_data_form(data, view, 'POST', request)
raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request)
raw_data_patch_form = self.get_raw_data_form(data, view, 'PATCH', request)
raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form response_headers = OrderedDict(sorted(response.items()))
renderer_content_type = ''
if renderer:
renderer_content_type = '%s' % renderer.media_type
if renderer.charset:
renderer_content_type += ' ;%s' % renderer.charset
response_headers['Content-Type'] = renderer_content_type if getattr(view, 'paginator', None) and view.paginator.display_page_controls:
paginator = view.paginator
else:
paginator = None csrf_cookie_name = settings.CSRF_COOKIE_NAME
csrf_header_name = settings.CSRF_HEADER_NAME
if csrf_header_name.startswith('HTTP_'):
csrf_header_name = csrf_header_name[5:]
csrf_header_name = csrf_header_name.replace('_', '-') context = {
'content': self.get_content(renderer, data, accepted_media_type, renderer_context),
'code_style': pygments_css(self.code_style),
'view': view,
'request': request,
'response': response,
'user': request.user,
'description': self.get_description(view, response.status_code),
'name': self.get_name(view),
'version': VERSION,
'paginator': paginator,
'breadcrumblist': self.get_breadcrumbs(request),
'allowed_methods': view.allowed_methods,
'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes],
'response_headers': response_headers, 'put_form': self.get_rendered_html_form(data, view, 'PUT', request),
'post_form': self.get_rendered_html_form(data, view, 'POST', request),
'delete_form': self.get_rendered_html_form(data, view, 'DELETE', request),
'options_form': self.get_rendered_html_form(data, view, 'OPTIONS', request), 'extra_actions': self.get_extra_actions(view), 'filter_form': self.get_filter_form(data, view, request), 'raw_data_put_form': raw_data_put_form,
'raw_data_post_form': raw_data_post_form,
'raw_data_patch_form': raw_data_patch_form,
'raw_data_put_or_patch_form': raw_data_put_or_patch_form, 'display_edit_forms': bool(response.status_code != 403), 'api_settings': api_settings,
'csrf_cookie_name': csrf_cookie_name,
'csrf_header_name': csrf_header_name
}
return context def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render the HTML for the browsable API representation.
"""
self.accepted_media_type = accepted_media_type or ''
self.renderer_context = renderer_context or {} template = loader.get_template(self.template)
context = self.get_context(data, accepted_media_type, renderer_context)
ret = template.render(context, request=renderer_context['request']) # Munge DELETE Response code to allow us to return content
# (Do this *after* we've rendered the template so that we include
# the normal deletion response code in the output)
response = renderer_context['response']
if response.status_code == status.HTTP_204_NO_CONTENT:
response.status_code = status.HTTP_200_OK return ret

BrowsableAPIRenderer

4、AdminRenderer

将数据呈现为 HTML,以显示类似管理员的内容,该渲染器适用于 CRUD 风格的 Web API,该 API 还应提供用于管理数据的用户友好界面。

class AdminRenderer(BrowsableAPIRenderer):
template = 'rest_framework/admin.html'
format = 'admin' def render(self, data, accepted_media_type=None, renderer_context=None):
self.accepted_media_type = accepted_media_type or ''
self.renderer_context = renderer_context or {} response = renderer_context['response']
request = renderer_context['request']
view = self.renderer_context['view'] if response.status_code == status.HTTP_400_BAD_REQUEST:
# Errors still need to display the list or detail information.
# The only way we can get at that is to simulate a GET request.
self.error_form = self.get_rendered_html_form(data, view, request.method, request)
self.error_title = {'POST': 'Create', 'PUT': 'Edit'}.get(request.method, 'Errors') with override_method(view, request, 'GET') as request:
response = view.get(request, *view.args, **view.kwargs)
data = response.data template = loader.get_template(self.template)
context = self.get_context(data, accepted_media_type, renderer_context)
ret = template.render(context, request=renderer_context['request']) # Creation and deletion should use redirects in the admin style.
if response.status_code == status.HTTP_201_CREATED and 'Location' in response:
response.status_code = status.HTTP_303_SEE_OTHER
response['Location'] = request.build_absolute_uri()
ret = '' if response.status_code == status.HTTP_204_NO_CONTENT:
response.status_code = status.HTTP_303_SEE_OTHER
try:
# Attempt to get the parent breadcrumb URL.
response['Location'] = self.get_breadcrumbs(request)[-2][1]
except KeyError:
# Otherwise reload current URL to get a 'Not Found' page.
response['Location'] = request.full_path
ret = '' return ret def get_context(self, data, accepted_media_type, renderer_context):
"""
Render the HTML for the browsable API representation.
"""
context = super(AdminRenderer, self).get_context(
data, accepted_media_type, renderer_context
) paginator = getattr(context['view'], 'paginator', None)
if paginator is not None and data is not None:
try:
results = paginator.get_results(data)
except (TypeError, KeyError):
results = data
else:
results = data if results is None:
header = {}
style = 'detail'
elif isinstance(results, list):
header = results[0] if results else {}
style = 'list'
else:
header = results
style = 'detail' columns = [key for key in header if key != 'url']
details = [key for key in header if key != 'url'] if isinstance(results, list) and 'view' in renderer_context:
for result in results:
url = self.get_result_url(result, context['view'])
if url is not None:
result.setdefault('url', url) context['style'] = style
context['columns'] = columns
context['details'] = details
context['results'] = results
context['error_form'] = getattr(self, 'error_form', None)
context['error_title'] = getattr(self, 'error_title', None)
return context def get_result_url(self, result, view):
"""
Attempt to reverse the result's detail view URL. This only works with views that are generic-like (has `.lookup_field`)
and viewset-like (has `.basename` / `.reverse_action()`).
"""
if not hasattr(view, 'reverse_action') or \
not hasattr(view, 'lookup_field'):
return lookup_field = view.lookup_field
lookup_url_kwarg = getattr(view, 'lookup_url_kwarg', None) or lookup_field try:
kwargs = {lookup_url_kwarg: result[lookup_field]}
return view.reverse_action('detail', kwargs=kwargs)
except (KeyError, NoReverseMatch):
return

AdminRenderer

更多请参考:https://www.django-rest-framework.org/api-guide/renderers/#api-reference

二、源码

首先,请求进来后还是会走到APIView的dispatch方法,这在之前的认证权限中已经详细说明:

1、dispatch

    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
#rest-framework重构request对象
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs) # Get the appropriate handler method
#这里和CBV一样进行方法的分发
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) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response

2、initial

    def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
#与渲染器相关
request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted
self.perform_authentication(request) #进行认证
self.check_permissions(request)
self.check_throttles(request)

3、perform_content_negotiation

在这个方法中获取视图中配置的渲染器,并且通过select_renderer返回合适的渲染器

    def perform_content_negotiation(self, request, force=False):
"""
Determine which renderer and media type to use render the response.
"""
renderers = self.get_renderers()
conneg = self.get_content_negotiator() try:
return conneg.select_renderer(request, renderers, self.format_kwarg)
except Exception:
if force:
return (renderers[0], renderers[0].media_type)
raise

perform_content_negotiation

    def select_renderer(self, request, renderers, format_suffix=None):
"""
Given a request and a list of renderers, return a two-tuple of:
(renderer, media type).
"""
# Allow URL style format override. eg. "?format=json
format_query_param = self.settings.URL_FORMAT_OVERRIDE
format = format_suffix or request.query_params.get(format_query_param) if format:
renderers = self.filter_renderers(renderers, format) accepts = self.get_accept_list(request) # Check the acceptable media types against each renderer,
# attempting more specific media types first
# NB. The inner loop here isn't as bad as it first looks :)
# Worst case is we're looping over len(accept_list) * len(self.renderers)
for media_type_set in order_by_precedence(accepts):
for renderer in renderers:
for media_type in media_type_set:
if media_type_matches(renderer.media_type, media_type):
# Return the most specific media type as accepted.
media_type_wrapper = _MediaType(media_type)
if (
_MediaType(renderer.media_type).precedence >
media_type_wrapper.precedence
):
# Eg client requests '*/*'
# Accepted media type is 'application/json'
full_media_type = ';'.join(
(renderer.media_type,) +
tuple('{0}={1}'.format(
key, value.decode(HTTP_HEADER_ENCODING))
for key, value in media_type_wrapper.params.items()))
return renderer, full_media_type
else:
# Eg client requests 'application/json; indent=8'
# Accepted media type is 'application/json; indent=8'
return renderer, media_type raise exceptions.NotAcceptable(available_renderers=renderers)

select_renderer

4、将渲染器赋值给request对象

在initial方法中将获取的渲染器赋值给request对象

      # Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
#与渲染器相关
request.accepted_renderer, request.accepted_media_type = neg

5、调用Response

在给客户端返回数据过程中使用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(Response, self).__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 six.iteritems(headers):
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 = "{0}; charset={1}".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, six.text_type):
assert charset, (
'renderer returned unicode, and did not specify '
'a charset value.'
)
return bytes(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(Response, self).__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

Response

而在Response中使用了赋值给request对象的渲染器,通过反射取到request中的渲染器:

 renderer = getattr(self, 'accepted_renderer', None)

然后调用渲染器中的render方法:

ret = renderer.render(self.data, accepted_media_type, context)

假设使用的是JSONRender渲染器:

 def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render `data` into JSON, returning a bytestring.
"""
if data is None:
return bytes() renderer_context = renderer_context or {}
indent = self.get_indent(accepted_media_type, renderer_context) if indent is None:
separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS
else:
separators = INDENT_SEPARATORS ret = json.dumps(
data, cls=self.encoder_class,
indent=indent, ensure_ascii=self.ensure_ascii,
allow_nan=not self.strict, separators=separators
) # On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
# but if ensure_ascii=False, the return type is underspecified,
# and may (or may not) be unicode.
# On python 3.x json.dumps() returns unicode strings.
if isinstance(ret, six.text_type):
# We always fully escape \u2028 and \u2029 to ensure we output JSON
# that is a strict javascript subset. If bytes were returned
# by json.dumps() then we don't have these characters in any case.
# See: http://timelessrepo.com/json-isnt-a-javascript-subset
ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
return bytes(ret.encode('utf-8'))
return ret

通过json.dumps进行序列化返回JSON类型的数据结果。

总结:

  • 在dispatch方法中使用initial方法初始化渲染器,并且将其赋值给request对象
  • 在返回结果使用Response时,调用request中被赋值的渲染器对象,使用其render方法进行处理,最后返回结果

参考文档:https://q1mi.github.io/Django-REST-framework-documentation/api-guide/renderers_zh/#_1

rest framework之渲染器的更多相关文章

  1. Django REST Framework - 分页 - 渲染器 - 解析器

    为什么要使用分页? 我们数据表中可能会有成千上万条数据,当我们访问某张表的所有数据时,我们不太可能需要一次把所有的数据都展示出来,因为数据量很大,对服务端的内存压力比较大还有就是网络传输过程中耗时也会 ...

  2. rest framework 之渲染器

    根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件. 用户请求头: Accept:text/html,application/xhtml+xml,application/xml;q=0 ...

  3. Django rest framework(8)---- 视图和渲染器

    django rest framework 之视图 序列化器    PagerSerialiser from rest_framework import serializers from api im ...

  4. django的rest framework框架——分页、视图、路由、渲染器

    一.rest framework的分页 1.使用rest framework内置类PageNumberPagination实现分类 from django.conf.urls import url f ...

  5. Django REST framework - 解析器和渲染器

    目录 Django REST framework - 解析器和渲染器 解析器 Django中的数据解析 DRF中的解析器 渲染器 Django REST framework - 解析器和渲染器 解析器 ...

  6. 07 Django REST Framework 解析器与渲染器

    01-解析器 REST 框架包括一些内置的Parser类,允许你接受各种媒体类型的请求.还支持定义自己的自定义解析器,这使你可以灵活地设计API接受的媒体类型. 注意: 开发客户端应用程序时应该始终记 ...

  7. Django REST framework基础:解析器和渲染器

    解析器 解析器的作用 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据.本质就是对请求体中的数据进行解析. 在了解解析器之前,我们要先知道Accept以及ContentTy ...

  8. rest framework 解析器,渲染器

    解析器 解析器的作用 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据.本质就是对请求体中的数据进行解析. 请求体相关字段: Accept:指定了接收的数据类型 Conte ...

  9. Django REST framework 之分页,视图,路由,渲染器

    1.分页 2.视图 3.路由 4.渲染器 1.分页 方法一: from django.shortcuts import render from rest_framework.versioning im ...

随机推荐

  1. Paint的Gradient的用法(转)

    转自:https://www.jianshu.com/p/02b02c1696b2 Paint的Gradient的用法 嗯哼嗯哼嗯哼嗯哼 关注 2017.07.04 15:45* 字数 173 阅读 ...

  2. GCC -l选项:手动添加链接库

    链接器把多个二进制的目标文件(object file)链接成一个单独的可执行文件.在链接过程中,它必须把符号(变量名.函数名等一些列标识符)用对应的数据的内存地址(变量地址.函数地址等)替代,以完成程 ...

  3. css 设置背景图片透明

    最终效果: 背景图片css不变,再背景图片的里层元素设置css样式即可 opacity:0.4; filter:alpha(opacity=40); /* 针对 IE8 以及更早的版本 */

  4. Python自动补全缩写意义

    自动补全的变量的类别p:parameter 参数 m:method 方法(类实例方法)调用方式classA aa.method()或者classA().method() c:class 类 v:var ...

  5. The user specified as a definer ('root'@'%') does not exist解决

    转自:https://www.cnblogs.com/hanxue53/p/5850263.html 解决方法: 2.进入mysql的安装路径之前,要确保你的mysql服务是开启的: 在DOS命令窗口 ...

  6. Django学习笔记(三)视图

    构建网页内容 视图函数的return具有多种响应类型: 上述函数主要来自django.http,该模块是实现响应功能的核心. 实际开发中可用此模块实现文件下载功能,在index的urls.py和vie ...

  7. Qt 无法连接mysql数据库的问题

    错误信息: QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: available drivers: QSQLITE QMYSQL QMYSQL ...

  8. 楼房重建 (rebuild)

    楼房重建 (rebuild) 题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子.为了简化问题 ...

  9. 【缓存与性能优化】方法论:如何优化一个Web系统的性能

    1.性能简介 一个网络请求的等待: 减少等待时间方式 不同位置对性能的操控 开发人员优化性能的方式 性能指标 2.缓存 缓存是把一些需要计算或者访问数据库的数据,放到内存中,当客户端访问时,直接从内存 ...

  10. MySQL 5.7免安装版设置编码格式、设置root用户密码 远程登录.

    一.设置默认编码格式为utf-8 ... 由于免安装版并没有my.ini的配置文件.需要自行粘贴配置并创建一个my.ini 配置如下: [mysql] # 设置mysql客户端默认字符集 defaul ...