mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 18:08:03 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			147 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			5.5 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 __future__ import unicode_literals
 | 
						|
 | 
						|
from django.core.exceptions import PermissionDenied
 | 
						|
from django.http import Http404
 | 
						|
from django.utils.encoding import force_text
 | 
						|
 | 
						|
from rest_framework import exceptions, serializers
 | 
						|
from rest_framework.compat import OrderedDict
 | 
						|
from rest_framework.request import clone_request
 | 
						|
from rest_framework.utils.field_mapping import ClassLookupDict
 | 
						|
 | 
						|
 | 
						|
class BaseMetadata(object):
 | 
						|
    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.NullBooleanField: 'boolean',
 | 
						|
        serializers.CharField: '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.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 = OrderedDict()
 | 
						|
        metadata['name'] = view.get_view_name()
 | 
						|
        metadata['description'] = view.get_view_description()
 | 
						|
        metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes]
 | 
						|
        metadata['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 set(['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 OrderedDict([
 | 
						|
            (field_name, self.get_field_info(field))
 | 
						|
            for field_name, field in serializer.fields.items()
 | 
						|
        ])
 | 
						|
 | 
						|
    def get_field_info(self, field):
 | 
						|
        """
 | 
						|
        Given an instance of a serializer field, return a dictionary
 | 
						|
        of metadata about it.
 | 
						|
        """
 | 
						|
        field_info = OrderedDict()
 | 
						|
        field_info['type'] = self.label_lookup[field]
 | 
						|
        field_info['required'] = getattr(field, 'required', False)
 | 
						|
 | 
						|
        attrs = [
 | 
						|
            'read_only', 'label', 'help_text',
 | 
						|
            'min_length', 'max_length',
 | 
						|
            'min_value', 'max_value'
 | 
						|
        ]
 | 
						|
 | 
						|
        for attr in attrs:
 | 
						|
            value = getattr(field, attr, None)
 | 
						|
            if value is not None and value != '':
 | 
						|
                field_info[attr] = force_text(value, strings_only=True)
 | 
						|
 | 
						|
        if getattr(field, 'child', None):
 | 
						|
            field_info['child'] = self.get_field_info(field.child)
 | 
						|
 | 
						|
        if not field_info.get('read_only') and hasattr(field, 'choices'):
 | 
						|
            field_info['choices'] = [
 | 
						|
                {
 | 
						|
                    'value': choice_value,
 | 
						|
                    'display_name': force_text(choice_name, strings_only=True)
 | 
						|
                }
 | 
						|
                for choice_value, choice_name in field.choices.items()
 | 
						|
            ]
 | 
						|
 | 
						|
        return field_info
 |