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

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. 

5 

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 

10 

11from django.core.exceptions import PermissionDenied 

12from django.http import Http404 

13from django.utils.encoding import force_str 

14 

15from rest_framework import exceptions, serializers 

16from rest_framework.request import clone_request 

17from rest_framework.utils.field_mapping import ClassLookupDict 

18 

19 

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.") 

27 

28 

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 }) 

60 

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 

72 

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 

97 

98 return actions 

99 

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 ]) 

114 

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) 

123 

124 attrs = [ 

125 'read_only', 'label', 'help_text', 

126 'min_length', 'max_length', 

127 'min_value', 'max_value' 

128 ] 

129 

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) 

134 

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) 

139 

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 ] 

150 

151 return field_info