mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			119 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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)
 |