2014-09-24 17:09:49 +04:00
|
|
|
"""
|
2014-12-04 05:11:42 +03:00
|
|
|
The metadata API is used to allow customization of how `OPTIONS` requests
|
2014-09-24 17:09:49 +04:00
|
|
|
are handled. We currently provide a single default implementation that returns
|
|
|
|
some fairly ad-hoc information about the view.
|
|
|
|
|
2014-12-04 05:11:42 +03:00
|
|
|
Future implementations might use JSON schema or other definitions in order
|
2014-09-24 17:09:49 +04:00
|
|
|
to return this information in a more standardized way.
|
|
|
|
"""
|
2015-09-22 17:35:38 +03:00
|
|
|
from collections import OrderedDict
|
|
|
|
|
2015-06-25 23:55:51 +03:00
|
|
|
from django.core.exceptions import PermissionDenied
|
2014-09-24 17:09:49 +04:00
|
|
|
from django.http import Http404
|
2019-07-30 03:02:43 +03:00
|
|
|
from django.utils.encoding import force_str
|
2015-06-18 16:38:29 +03:00
|
|
|
|
2014-09-24 17:09:49 +04:00
|
|
|
from rest_framework import exceptions, serializers
|
|
|
|
from rest_framework.request import clone_request
|
|
|
|
from rest_framework.utils.field_mapping import ClassLookupDict
|
|
|
|
|
|
|
|
|
2019-04-30 18:53:44 +03:00
|
|
|
class BaseMetadata:
|
2014-09-24 17:09:49 +04:00
|
|
|
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',
|
2018-03-13 17:49:15 +03:00
|
|
|
serializers.UUIDField: 'string',
|
2014-09-24 17:09:49 +04:00
|
|
|
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',
|
2022-11-21 17:33:19 +03:00
|
|
|
serializers.DurationField: 'duration',
|
2014-09-24 17:09:49 +04:00
|
|
|
serializers.ChoiceField: 'choice',
|
|
|
|
serializers.MultipleChoiceField: 'multiple choice',
|
|
|
|
serializers.FileField: 'file upload',
|
|
|
|
serializers.ImageField: 'image upload',
|
2015-07-02 14:55:42 +03:00
|
|
|
serializers.ListField: 'list',
|
|
|
|
serializers.DictField: 'nested object',
|
|
|
|
serializers.Serializer: 'nested object',
|
2014-09-24 17:09:49 +04:00
|
|
|
})
|
|
|
|
|
|
|
|
def determine_metadata(self, request, view):
|
2014-11-06 15:00:30 +03:00
|
|
|
metadata = OrderedDict()
|
2014-09-24 17:09:49 +04:00
|
|
|
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 = {}
|
2015-10-17 12:00:11 +03:00
|
|
|
for method in {'PUT', 'POST'} & set(view.allowed_methods):
|
2014-09-24 17:09:49 +04:00
|
|
|
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.
|
|
|
|
"""
|
2014-11-07 15:16:25 +03:00
|
|
|
if hasattr(serializer, 'child'):
|
|
|
|
# If this is a `ListSerializer` then we want to examine the
|
|
|
|
# underlying child serializer instance instead.
|
|
|
|
serializer = serializer.child
|
2014-11-06 15:00:30 +03:00
|
|
|
return OrderedDict([
|
2014-09-24 17:09:49 +04:00
|
|
|
(field_name, self.get_field_info(field))
|
2014-09-25 14:04:18 +04:00
|
|
|
for field_name, field in serializer.fields.items()
|
2018-03-24 01:47:10 +03:00
|
|
|
if not isinstance(field, serializers.HiddenField)
|
2014-09-24 17:09:49 +04:00
|
|
|
])
|
|
|
|
|
|
|
|
def get_field_info(self, field):
|
|
|
|
"""
|
|
|
|
Given an instance of a serializer field, return a dictionary
|
|
|
|
of metadata about it.
|
|
|
|
"""
|
2014-11-06 15:00:30 +03:00
|
|
|
field_info = OrderedDict()
|
2014-09-24 17:09:49 +04:00
|
|
|
field_info['type'] = self.label_lookup[field]
|
|
|
|
field_info['required'] = getattr(field, 'required', False)
|
|
|
|
|
2015-02-20 18:31:12 +03:00
|
|
|
attrs = [
|
|
|
|
'read_only', 'label', 'help_text',
|
|
|
|
'min_length', 'max_length',
|
2023-04-27 07:24:13 +03:00
|
|
|
'min_value', 'max_value',
|
|
|
|
'max_digits', 'decimal_places'
|
2015-02-20 18:31:12 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
for attr in attrs:
|
2014-09-24 17:09:49 +04:00
|
|
|
value = getattr(field, attr, None)
|
|
|
|
if value is not None and value != '':
|
2019-07-30 03:02:43 +03:00
|
|
|
field_info[attr] = force_str(value, strings_only=True)
|
2014-09-24 17:09:49 +04:00
|
|
|
|
2015-07-02 15:15:43 +03:00
|
|
|
if getattr(field, 'child', None):
|
|
|
|
field_info['child'] = self.get_field_info(field.child)
|
2015-07-16 17:17:18 +03:00
|
|
|
elif getattr(field, 'fields', None):
|
|
|
|
field_info['children'] = self.get_serializer_info(field)
|
2015-07-02 15:15:43 +03:00
|
|
|
|
2016-06-01 18:33:03 +03:00
|
|
|
if (not field_info.get('read_only') and
|
2016-06-01 19:28:32 +03:00
|
|
|
not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) and
|
2016-06-01 18:33:03 +03:00
|
|
|
hasattr(field, 'choices')):
|
2014-09-24 17:09:49 +04:00
|
|
|
field_info['choices'] = [
|
2014-11-18 18:42:52 +03:00
|
|
|
{
|
|
|
|
'value': choice_value,
|
2019-07-30 03:02:43 +03:00
|
|
|
'display_name': force_str(choice_name, strings_only=True)
|
2014-11-18 18:42:52 +03:00
|
|
|
}
|
2014-09-24 17:09:49 +04:00
|
|
|
for choice_value, choice_name in field.choices.items()
|
|
|
|
]
|
|
|
|
|
|
|
|
return field_info
|