mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-01 00:17:40 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			153 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| The metadata API is used to allow customization of how `OPTIONS` requests
 | |
| are handled. We currently provide a single default implementation that returns
 | |
| some fairly ad-hoc information about the view.
 | |
| 
 | |
| Future implementations might use JSON schema or other definitions in order
 | |
| to return this information in a more standardized way.
 | |
| """
 | |
| from django.core.exceptions import PermissionDenied
 | |
| from django.http import Http404
 | |
| from django.utils.encoding import force_str
 | |
| 
 | |
| from rest_framework import exceptions, serializers
 | |
| from rest_framework.request import clone_request
 | |
| from rest_framework.utils.field_mapping import ClassLookupDict
 | |
| 
 | |
| 
 | |
| class BaseMetadata:
 | |
|     def determine_metadata(self, request, view):
 | |
|         """
 | |
|         Return a dictionary of metadata about the view.
 | |
|         Used to return responses for OPTIONS requests.
 | |
|         """
 | |
|         raise NotImplementedError(".determine_metadata() must be overridden.")
 | |
| 
 | |
| 
 | |
| class SimpleMetadata(BaseMetadata):
 | |
|     """
 | |
|     This is the default metadata implementation.
 | |
|     It returns an ad-hoc set of information about the view.
 | |
|     There are not any formalized standards for `OPTIONS` responses
 | |
|     for us to base this on.
 | |
|     """
 | |
|     label_lookup = ClassLookupDict({
 | |
|         serializers.Field: 'field',
 | |
|         serializers.BooleanField: 'boolean',
 | |
|         serializers.CharField: 'string',
 | |
|         serializers.UUIDField: 'string',
 | |
|         serializers.URLField: 'url',
 | |
|         serializers.EmailField: 'email',
 | |
|         serializers.RegexField: 'regex',
 | |
|         serializers.SlugField: 'slug',
 | |
|         serializers.IntegerField: 'integer',
 | |
|         serializers.FloatField: 'float',
 | |
|         serializers.DecimalField: 'decimal',
 | |
|         serializers.DateField: 'date',
 | |
|         serializers.DateTimeField: 'datetime',
 | |
|         serializers.TimeField: 'time',
 | |
|         serializers.DurationField: 'duration',
 | |
|         serializers.ChoiceField: 'choice',
 | |
|         serializers.MultipleChoiceField: 'multiple choice',
 | |
|         serializers.FileField: 'file upload',
 | |
|         serializers.ImageField: 'image upload',
 | |
|         serializers.ListField: 'list',
 | |
|         serializers.DictField: 'nested object',
 | |
|         serializers.Serializer: 'nested object',
 | |
|     })
 | |
| 
 | |
|     def determine_metadata(self, request, view):
 | |
|         metadata = {
 | |
|             "name": view.get_view_name(),
 | |
|             "description": view.get_view_description(),
 | |
|             "renders": [renderer.media_type for renderer in view.renderer_classes],
 | |
|             "parses": [parser.media_type for parser in view.parser_classes],
 | |
|         }
 | |
|         if hasattr(view, 'get_serializer'):
 | |
|             actions = self.determine_actions(request, view)
 | |
|             if actions:
 | |
|                 metadata['actions'] = actions
 | |
|         return metadata
 | |
| 
 | |
|     def determine_actions(self, request, view):
 | |
|         """
 | |
|         For generic class based views we return information about
 | |
|         the fields that are accepted for 'PUT' and 'POST' methods.
 | |
|         """
 | |
|         actions = {}
 | |
|         for method in {'PUT', 'POST'} & set(view.allowed_methods):
 | |
|             view.request = clone_request(request, method)
 | |
|             try:
 | |
|                 # Test global permissions
 | |
|                 if hasattr(view, 'check_permissions'):
 | |
|                     view.check_permissions(view.request)
 | |
|                 # Test object permissions
 | |
|                 if method == 'PUT' and hasattr(view, 'get_object'):
 | |
|                     view.get_object()
 | |
|             except (exceptions.APIException, PermissionDenied, Http404):
 | |
|                 pass
 | |
|             else:
 | |
|                 # If user has appropriate permissions for the view, include
 | |
|                 # appropriate metadata about the fields that should be supplied.
 | |
|                 serializer = view.get_serializer()
 | |
|                 actions[method] = self.get_serializer_info(serializer)
 | |
|             finally:
 | |
|                 view.request = request
 | |
| 
 | |
|         return actions
 | |
| 
 | |
|     def get_serializer_info(self, serializer):
 | |
|         """
 | |
|         Given an instance of a serializer, return a dictionary of metadata
 | |
|         about its fields.
 | |
|         """
 | |
|         if hasattr(serializer, 'child'):
 | |
|             # If this is a `ListSerializer` then we want to examine the
 | |
|             # underlying child serializer instance instead.
 | |
|             serializer = serializer.child
 | |
|         return {
 | |
|             field_name: self.get_field_info(field)
 | |
|             for field_name, field in serializer.fields.items()
 | |
|             if not isinstance(field, serializers.HiddenField)
 | |
|         }
 | |
| 
 | |
|     def get_field_info(self, field):
 | |
|         """
 | |
|         Given an instance of a serializer field, return a dictionary
 | |
|         of metadata about it.
 | |
|         """
 | |
|         field_info = {
 | |
|             "type": self.label_lookup[field],
 | |
|             "required": getattr(field, "required", False),
 | |
|         }
 | |
| 
 | |
|         attrs = [
 | |
|             'read_only', 'label', 'help_text',
 | |
|             'min_length', 'max_length',
 | |
|             'min_value', 'max_value',
 | |
|             'max_digits', 'decimal_places'
 | |
|         ]
 | |
| 
 | |
|         for attr in attrs:
 | |
|             value = getattr(field, attr, None)
 | |
|             if value is not None and value != '':
 | |
|                 field_info[attr] = force_str(value, strings_only=True)
 | |
| 
 | |
|         if getattr(field, 'child', None):
 | |
|             field_info['child'] = self.get_field_info(field.child)
 | |
|         elif getattr(field, 'fields', None):
 | |
|             field_info['children'] = self.get_serializer_info(field)
 | |
| 
 | |
|         if (not field_info.get('read_only') and
 | |
|             not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) and
 | |
|                 hasattr(field, 'choices')):
 | |
|             field_info['choices'] = [
 | |
|                 {
 | |
|                     'value': choice_value,
 | |
|                     'display_name': force_str(choice_name, strings_only=True)
 | |
|                 }
 | |
|                 for choice_value, choice_name in field.choices.items()
 | |
|             ]
 | |
| 
 | |
|         return field_info
 |