Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/negotiation.py: 69%
40 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
2Content negotiation deals with selecting an appropriate renderer given the
3incoming request. Typically this will be based on the request's Accept header.
4"""
5from django.http import Http404
7from rest_framework import HTTP_HEADER_ENCODING, exceptions
8from rest_framework.settings import api_settings
9from rest_framework.utils.mediatypes import (
10 _MediaType, media_type_matches, order_by_precedence
11)
14class BaseContentNegotiation:
15 def select_parser(self, request, parsers):
16 raise NotImplementedError('.select_parser() must be implemented')
18 def select_renderer(self, request, renderers, format_suffix=None):
19 raise NotImplementedError('.select_renderer() must be implemented')
22class DefaultContentNegotiation(BaseContentNegotiation):
23 settings = api_settings
25 def select_parser(self, request, parsers):
26 """
27 Given a list of parsers and a media type, return the appropriate
28 parser to handle the incoming request.
29 """
30 for parser in parsers: 30 ↛ 33line 30 didn't jump to line 33, because the loop on line 30 didn't complete
31 if media_type_matches(parser.media_type, request.content_type):
32 return parser
33 return None
35 def select_renderer(self, request, renderers, format_suffix=None):
36 """
37 Given a request and a list of renderers, return a two-tuple of:
38 (renderer, media type).
39 """
40 # Allow URL style format override. eg. "?format=json
41 format_query_param = self.settings.URL_FORMAT_OVERRIDE
42 format = format_suffix or request.query_params.get(format_query_param)
44 if format: 44 ↛ 45line 44 didn't jump to line 45, because the condition on line 44 was never true
45 renderers = self.filter_renderers(renderers, format)
47 accepts = self.get_accept_list(request)
49 # Check the acceptable media types against each renderer,
50 # attempting more specific media types first
51 # NB. The inner loop here isn't as bad as it first looks :)
52 # Worst case is we're looping over len(accept_list) * len(self.renderers)
53 for media_type_set in order_by_precedence(accepts): 53 ↛ 76line 53 didn't jump to line 76, because the loop on line 53 didn't complete
54 for renderer in renderers: 54 ↛ 53line 54 didn't jump to line 53, because the loop on line 54 didn't complete
55 for media_type in media_type_set: 55 ↛ 54line 55 didn't jump to line 54, because the loop on line 55 didn't complete
56 if media_type_matches(renderer.media_type, media_type): 56 ↛ 55line 56 didn't jump to line 55, because the condition on line 56 was never false
57 # Return the most specific media type as accepted.
58 media_type_wrapper = _MediaType(media_type)
59 if ( 59 ↛ 74line 59 didn't jump to line 74
60 _MediaType(renderer.media_type).precedence >
61 media_type_wrapper.precedence
62 ):
63 # Eg client requests '*/*'
64 # Accepted media type is 'application/json'
65 full_media_type = ';'.join(
66 (renderer.media_type,) +
67 tuple('{}={}'.format(
68 key, value.decode(HTTP_HEADER_ENCODING))
69 for key, value in media_type_wrapper.params.items()))
70 return renderer, full_media_type
71 else:
72 # Eg client requests 'application/json; indent=8'
73 # Accepted media type is 'application/json; indent=8'
74 return renderer, media_type
76 raise exceptions.NotAcceptable(available_renderers=renderers)
78 def filter_renderers(self, renderers, format):
79 """
80 If there is a '.json' style format suffix, filter the renderers
81 so that we only negotiation against those that accept that format.
82 """
83 renderers = [renderer for renderer in renderers
84 if renderer.format == format]
85 if not renderers:
86 raise Http404
87 return renderers
89 def get_accept_list(self, request):
90 """
91 Given the incoming request, return a tokenized list of media
92 type strings.
93 """
94 header = request.META.get('HTTP_ACCEPT', '*/*')
95 return [token.strip() for token in header.split(',')]