rest framework之渲染器
一、内置渲染器
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之渲染器的更多相关文章
- Django REST Framework - 分页 - 渲染器 - 解析器
为什么要使用分页? 我们数据表中可能会有成千上万条数据,当我们访问某张表的所有数据时,我们不太可能需要一次把所有的数据都展示出来,因为数据量很大,对服务端的内存压力比较大还有就是网络传输过程中耗时也会 ...
- rest framework 之渲染器
根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件. 用户请求头: Accept:text/html,application/xhtml+xml,application/xml;q=0 ...
- Django rest framework(8)---- 视图和渲染器
django rest framework 之视图 序列化器 PagerSerialiser from rest_framework import serializers from api im ...
- django的rest framework框架——分页、视图、路由、渲染器
一.rest framework的分页 1.使用rest framework内置类PageNumberPagination实现分类 from django.conf.urls import url f ...
- Django REST framework - 解析器和渲染器
目录 Django REST framework - 解析器和渲染器 解析器 Django中的数据解析 DRF中的解析器 渲染器 Django REST framework - 解析器和渲染器 解析器 ...
- 07 Django REST Framework 解析器与渲染器
01-解析器 REST 框架包括一些内置的Parser类,允许你接受各种媒体类型的请求.还支持定义自己的自定义解析器,这使你可以灵活地设计API接受的媒体类型. 注意: 开发客户端应用程序时应该始终记 ...
- Django REST framework基础:解析器和渲染器
解析器 解析器的作用 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据.本质就是对请求体中的数据进行解析. 在了解解析器之前,我们要先知道Accept以及ContentTy ...
- rest framework 解析器,渲染器
解析器 解析器的作用 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据.本质就是对请求体中的数据进行解析. 请求体相关字段: Accept:指定了接收的数据类型 Conte ...
- Django REST framework 之分页,视图,路由,渲染器
1.分页 2.视图 3.路由 4.渲染器 1.分页 方法一: from django.shortcuts import render from rest_framework.versioning im ...
随机推荐
- windows下如何使用Git上传代码
首先,在使用Git的同时,我们需要拥有码云账号,在官网注册即可(官网:https://gitee.com/). 注册结束后创建一个代码仓库,最好和要上传的文件夹名字一样: 1.首先在电脑上安装wind ...
- linux IO模式以及select、poll、epoll详解
- Qt 【widget如何铺满窗口】
刚接触qt不是很长时间,都是使用ui拖拽控件实现界面,然后发现有些问题就是控件一旦多了起来,拖拽就不好控制了,然后就转而使用纯代码开发. 一下是碰到第一个问题: 创建一个MainWidget; Mai ...
- org.zkoss.zul.Filedownload.java 源码
/* Filedownload.java Purpose: Description: History: Mon Apr 16 09:29:44 2007, Created by tomyeh Copy ...
- .net文件下载的四种方法
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Secu ...
- Mac启动时:boot task failed:fsck-safe处理办法
mac系统启动到一半,然后突然关机,查看启动信息发现:boot task failed:fsck-safe 处理方法,clover启动的时候按空格,然后选择sigle mode. 进入使用命令:fsc ...
- kafka ProducerConfig 配置
kafka-clients : 1.0.1
- 25. 服务器性能监控之nmon工具介绍
nmon介绍: nmon是一个简单的性能监测工具,可以监测CPU.内存.网络等的使用情况. 步骤: 1.下载nmon(根据你的操作系统下载),地址 2.nmon文件部署到服务器中 3.启动nmon(注 ...
- 数据库(二)—— MySQL索引优化
目录 MySQL的索引优化 一.MySQL 5.7的初始化配置 二.MySQL配置文件 1.配置 2.配置文件作用 三.多实例 1.创建相关的目录 2.创建实例的配置文件 3.初始化 4.授权 5.启 ...
- CFile CStdioFile CArchive 文件操作之异同(详细)
两者的主要区别: 一. CFile类操作文件默认的是Binary模式,CStdioFile类操作文件默认的是Text模式. 在Binary模式下我们必须输入'\r\n',才能起到回车换行的效果, ...