Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/metadata.py: 21%
58 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
2The metadata API is used to allow customization of how `OPTIONS` requests
3are handled. We currently provide a single default implementation that returns
4some fairly ad-hoc information about the view.
6Future implementations might use JSON schema or other definitions in order
7to return this information in a more standardized way.
8"""
9from collections import OrderedDict
11from django.core.exceptions import PermissionDenied
12from django.http import Http404
13from django.utils.encoding import force_str
15from rest_framework import exceptions, serializers
16from rest_framework.request import clone_request
17from rest_framework.utils.field_mapping import ClassLookupDict
20class BaseMetadata:
21 def determine_metadata(self, request, view):
22 """
23 Return a dictionary of metadata about the view.
24 Used to return responses for OPTIONS requests.
25 """
26 raise NotImplementedError(".determine_metadata() must be overridden.")
29class SimpleMetadata(BaseMetadata):
30 """
31 This is the default metadata implementation.
32 It returns an ad-hoc set of information about the view.
33 There are not any formalized standards for `OPTIONS` responses
34 for us to base this on.
35 """
36 label_lookup = ClassLookupDict({
37 serializers.Field: 'field',
38 serializers.BooleanField: 'boolean',
39 serializers.NullBooleanField: 'boolean',
40 serializers.CharField: 'string',
41 serializers.UUIDField: 'string',
42 serializers.URLField: 'url',
43 serializers.EmailField: 'email',
44 serializers.RegexField: 'regex',
45 serializers.SlugField: 'slug',
46 serializers.IntegerField: 'integer',
47 serializers.FloatField: 'float',
48 serializers.DecimalField: 'decimal',
49 serializers.DateField: 'date',
50 serializers.DateTimeField: 'datetime',
51 serializers.TimeField: 'time',
52 serializers.ChoiceField: 'choice',
53 serializers.MultipleChoiceField: 'multiple choice',
54 serializers.FileField: 'file upload',
55 serializers.ImageField: 'image upload',
56 serializers.ListField: 'list',
57 serializers.DictField: 'nested object',
58 serializers.Serializer: 'nested object',
59 })
61 def determine_metadata(self, request, view):
62 metadata = OrderedDict()
63 metadata['name'] = view.get_view_name()
64 metadata['description'] = view.get_view_description()
65 metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes]
66 metadata['parses'] = [parser.media_type for parser in view.parser_classes]
67 if hasattr(view, 'get_serializer'):
68 actions = self.determine_actions(request, view)
69 if actions:
70 metadata['actions'] = actions
71 return metadata
73 def determine_actions(self, request, view):
74 """
75 For generic class based views we return information about
76 the fields that are accepted for 'PUT' and 'POST' methods.
77 """
78 actions = {}
79 for method in {'PUT', 'POST'} & set(view.allowed_methods):
80 view.request = clone_request(request, method)
81 try:
82 # Test global permissions
83 if hasattr(view, 'check_permissions'):
84 view.check_permissions(view.request)
85 # Test object permissions
86 if method == 'PUT' and hasattr(view, 'get_object'):
87 view.get_object()
88 except (exceptions.APIException, PermissionDenied, Http404):
89 pass
90 else:
91 # If user has appropriate permissions for the view, include
92 # appropriate metadata about the fields that should be supplied.
93 serializer = view.get_serializer()
94 actions[method] = self.get_serializer_info(serializer)
95 finally:
96 view.request = request
98 return actions
100 def get_serializer_info(self, serializer):
101 """
102 Given an instance of a serializer, return a dictionary of metadata
103 about its fields.
104 """
105 if hasattr(serializer, 'child'):
106 # If this is a `ListSerializer` then we want to examine the
107 # underlying child serializer instance instead.
108 serializer = serializer.child
109 return OrderedDict([
110 (field_name, self.get_field_info(field))
111 for field_name, field in serializer.fields.items()
112 if not isinstance(field, serializers.HiddenField)
113 ])
115 def get_field_info(self, field):
116 """
117 Given an instance of a serializer field, return a dictionary
118 of metadata about it.
119 """
120 field_info = OrderedDict()
121 field_info['type'] = self.label_lookup[field]
122 field_info['required'] = getattr(field, 'required', False)
124 attrs = [
125 'read_only', 'label', 'help_text',
126 'min_length', 'max_length',
127 'min_value', 'max_value'
128 ]
130 for attr in attrs:
131 value = getattr(field, attr, None)
132 if value is not None and value != '':
133 field_info[attr] = force_str(value, strings_only=True)
135 if getattr(field, 'child', None):
136 field_info['child'] = self.get_field_info(field.child)
137 elif getattr(field, 'fields', None):
138 field_info['children'] = self.get_serializer_info(field)
140 if (not field_info.get('read_only') and
141 not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) and
142 hasattr(field, 'choices')):
143 field_info['choices'] = [
144 {
145 'value': choice_value,
146 'display_name': force_str(choice_name, strings_only=True)
147 }
148 for choice_value, choice_name in field.choices.items()
149 ]
151 return field_info