File: urlpatterns.py

package info (click to toggle)
djangorestframework 3.16.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,092 kB
  • sloc: javascript: 31,965; python: 29,367; makefile: 32; sh: 6
file content (118 lines) | stat: -rw-r--r-- 4,401 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from django.urls import URLResolver, include, path, re_path, register_converter
from django.urls.converters import get_converters
from django.urls.resolvers import RoutePattern

from rest_framework.settings import api_settings


def _get_format_path_converter(allowed):
    if allowed:
        if len(allowed) == 1:
            allowed_pattern = allowed[0]
        else:
            allowed_pattern = '(?:%s)' % '|'.join(allowed)
        suffix_pattern = r"\.%s/?" % allowed_pattern
    else:
        suffix_pattern = r"\.[a-z0-9]+/?"

    class FormatSuffixConverter:
        regex = suffix_pattern

        def to_python(self, value):
            return value.strip('./')

        def to_url(self, value):
            return '.' + value + '/'

    return FormatSuffixConverter


def _generate_converter_name(allowed):
    converter_name = 'drf_format_suffix'
    if allowed:
        converter_name += '_' + '_'.join(allowed)
    return converter_name


def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None):
    ret = []
    for urlpattern in urlpatterns:
        if isinstance(urlpattern, URLResolver):
            # Set of included URL patterns
            regex = urlpattern.pattern.regex.pattern
            namespace = urlpattern.namespace
            app_name = urlpattern.app_name
            kwargs = urlpattern.default_kwargs
            # Add in the included patterns, after applying the suffixes
            patterns = apply_suffix_patterns(urlpattern.url_patterns,
                                             suffix_pattern,
                                             suffix_required,
                                             suffix_route)

            # if the original pattern was a RoutePattern we need to preserve it
            if isinstance(urlpattern.pattern, RoutePattern):
                assert path is not None
                route = str(urlpattern.pattern)
                new_pattern = path(route, include((patterns, app_name), namespace), kwargs)
            else:
                new_pattern = re_path(regex, include((patterns, app_name), namespace), kwargs)

            ret.append(new_pattern)
        else:
            # Regular URL pattern
            regex = urlpattern.pattern.regex.pattern.rstrip('$').rstrip('/') + suffix_pattern
            view = urlpattern.callback
            kwargs = urlpattern.default_args
            name = urlpattern.name
            # Add in both the existing and the new urlpattern
            if not suffix_required:
                ret.append(urlpattern)

            # if the original pattern was a RoutePattern we need to preserve it
            if isinstance(urlpattern.pattern, RoutePattern):
                assert path is not None
                assert suffix_route is not None
                route = str(urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route
                new_pattern = path(route, view, kwargs, name)
            else:
                new_pattern = re_path(regex, view, kwargs, name)

            ret.append(new_pattern)

    return ret


def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None):
    """
    Supplement existing urlpatterns with corresponding patterns that also
    include a '.format' suffix.  Retains urlpattern ordering.

    urlpatterns:
        A list of URL patterns.

    suffix_required:
        If `True`, only suffixed URLs will be generated, and non-suffixed
        URLs will not be used.  Defaults to `False`.

    allowed:
        An optional tuple/list of allowed suffixes.  eg ['json', 'api']
        Defaults to `None`, which allows any suffix.
    """
    suffix_kwarg = api_settings.FORMAT_SUFFIX_KWARG
    if allowed:
        if len(allowed) == 1:
            allowed_pattern = allowed[0]
        else:
            allowed_pattern = '(%s)' % '|'.join(allowed)
        suffix_pattern = r'\.(?P<%s>%s)/?$' % (suffix_kwarg, allowed_pattern)
    else:
        suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg

    converter_name = _generate_converter_name(allowed)
    if converter_name not in get_converters():
        suffix_converter = _get_format_path_converter(allowed)
        register_converter(suffix_converter, converter_name)

    suffix_route = '<%s:%s>' % (converter_name, suffix_kwarg)

    return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route)