17.12
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
from django.urls import get_script_prefix, resolve
|
||||
|
||||
|
||||
def get_breadcrumbs(url, request=None):
|
||||
"""
|
||||
Given a url returns a list of breadcrumbs, which are each a
|
||||
tuple of (name, url).
|
||||
"""
|
||||
from rest_framework.reverse import preserve_builtin_query_params
|
||||
from rest_framework.views import APIView
|
||||
|
||||
def breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen):
|
||||
"""
|
||||
Add tuples of (name, url) to the breadcrumbs list,
|
||||
progressively chomping off parts of the url.
|
||||
"""
|
||||
try:
|
||||
(view, unused_args, unused_kwargs) = resolve(url)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# Check if this is a REST framework view,
|
||||
# and if so add it to the breadcrumbs
|
||||
cls = getattr(view, 'cls', None)
|
||||
initkwargs = getattr(view, 'initkwargs', {})
|
||||
if cls is not None and issubclass(cls, APIView):
|
||||
# Don't list the same view twice in a row.
|
||||
# Probably an optional trailing slash.
|
||||
if not seen or seen[-1] != view:
|
||||
c = cls(**initkwargs)
|
||||
name = c.get_view_name()
|
||||
insert_url = preserve_builtin_query_params(prefix + url, request)
|
||||
breadcrumbs_list.insert(0, (name, insert_url))
|
||||
seen.append(view)
|
||||
|
||||
if url == '':
|
||||
# All done
|
||||
return breadcrumbs_list
|
||||
|
||||
elif url.endswith('/'):
|
||||
# Drop trailing slash off the end and continue to try to
|
||||
# resolve more breadcrumbs
|
||||
url = url.rstrip('/')
|
||||
return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen)
|
||||
|
||||
# Drop trailing non-slash off the end and continue to try to
|
||||
# resolve more breadcrumbs
|
||||
url = url[:url.rfind('/') + 1]
|
||||
return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen)
|
||||
|
||||
prefix = get_script_prefix().rstrip('/')
|
||||
url = url[len(prefix):]
|
||||
return breadcrumbs_recursive(url, [], prefix, [])
|
||||
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Handling of media types, as found in HTTP Content-Type and Accept headers.
|
||||
|
||||
See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
||||
"""
|
||||
from django.http.multipartparser import parse_header
|
||||
|
||||
from rest_framework import HTTP_HEADER_ENCODING
|
||||
|
||||
|
||||
def media_type_matches(lhs, rhs):
|
||||
"""
|
||||
Returns ``True`` if the media type in the first argument <= the
|
||||
media type in the second argument. The media types are strings
|
||||
as described by the HTTP spec.
|
||||
|
||||
Valid media type strings include:
|
||||
|
||||
'application/json; indent=4'
|
||||
'application/json'
|
||||
'text/*'
|
||||
'*/*'
|
||||
"""
|
||||
lhs = _MediaType(lhs)
|
||||
rhs = _MediaType(rhs)
|
||||
return lhs.match(rhs)
|
||||
|
||||
|
||||
def order_by_precedence(media_type_lst):
|
||||
"""
|
||||
Returns a list of sets of media type strings, ordered by precedence.
|
||||
Precedence is determined by how specific a media type is:
|
||||
|
||||
3. 'type/subtype; param=val'
|
||||
2. 'type/subtype'
|
||||
1. 'type/*'
|
||||
0. '*/*'
|
||||
"""
|
||||
ret = [set(), set(), set(), set()]
|
||||
for media_type in media_type_lst:
|
||||
precedence = _MediaType(media_type).precedence
|
||||
ret[3 - precedence].add(media_type)
|
||||
return [media_types for media_types in ret if media_types]
|
||||
|
||||
|
||||
class _MediaType:
|
||||
def __init__(self, media_type_str):
|
||||
self.orig = '' if (media_type_str is None) else media_type_str
|
||||
self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING))
|
||||
self.main_type, sep, self.sub_type = self.full_type.partition('/')
|
||||
|
||||
def match(self, other):
|
||||
"""Return true if this MediaType satisfies the given MediaType."""
|
||||
for key in self.params:
|
||||
if key != 'q' and other.params.get(key, None) != self.params.get(key, None):
|
||||
return False
|
||||
|
||||
if self.sub_type != '*' and other.sub_type != '*' and other.sub_type != self.sub_type:
|
||||
return False
|
||||
|
||||
if self.main_type != '*' and other.main_type != '*' and other.main_type != self.main_type:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def precedence(self):
|
||||
"""
|
||||
Return a precedence level from 0-3 for the media type given how specific it is.
|
||||
"""
|
||||
if self.main_type == '*':
|
||||
return 0
|
||||
elif self.sub_type == '*':
|
||||
return 1
|
||||
elif not self.params or list(self.params) == ['q']:
|
||||
return 2
|
||||
return 3
|
||||
|
||||
def __str__(self):
|
||||
ret = "%s/%s" % (self.main_type, self.sub_type)
|
||||
for key, val in self.params.items():
|
||||
ret += "; %s=%s" % (key, val.decode('ascii'))
|
||||
return ret
|
||||
Reference in New Issue
Block a user