Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/utils/serializer_helpers.py: 50%

89 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from collections import OrderedDict 

2from collections.abc import Mapping, MutableMapping 

3 

4from django.utils.encoding import force_str 

5 

6from rest_framework.utils import json 

7 

8 

9class ReturnDict(OrderedDict): 

10 """ 

11 Return object from `serializer.data` for the `Serializer` class. 

12 Includes a backlink to the serializer instance for renderers 

13 to use if they need richer field information. 

14 """ 

15 

16 def __init__(self, *args, **kwargs): 

17 self.serializer = kwargs.pop('serializer') 

18 super().__init__(*args, **kwargs) 

19 

20 def copy(self): 

21 return ReturnDict(self, serializer=self.serializer) 

22 

23 def __repr__(self): 

24 return dict.__repr__(self) 

25 

26 def __reduce__(self): 

27 # Pickling these objects will drop the .serializer backlink, 

28 # but preserve the raw data. 

29 return (dict, (dict(self),)) 

30 

31 

32class ReturnList(list): 

33 """ 

34 Return object from `serializer.data` for the `SerializerList` class. 

35 Includes a backlink to the serializer instance for renderers 

36 to use if they need richer field information. 

37 """ 

38 

39 def __init__(self, *args, **kwargs): 

40 self.serializer = kwargs.pop('serializer') 

41 super().__init__(*args, **kwargs) 

42 

43 def __repr__(self): 

44 return list.__repr__(self) 

45 

46 def __reduce__(self): 

47 # Pickling these objects will drop the .serializer backlink, 

48 # but preserve the raw data. 

49 return (list, (list(self),)) 

50 

51 

52class BoundField: 

53 """ 

54 A field object that also includes `.value` and `.error` properties. 

55 Returned when iterating over a serializer instance, 

56 providing an API similar to Django forms and form fields. 

57 """ 

58 

59 def __init__(self, field, value, errors, prefix=''): 

60 self._field = field 

61 self._prefix = prefix 

62 self.value = value 

63 self.errors = errors 

64 self.name = prefix + self.field_name 

65 

66 def __getattr__(self, attr_name): 

67 return getattr(self._field, attr_name) 

68 

69 @property 

70 def _proxy_class(self): 

71 return self._field.__class__ 

72 

73 def __repr__(self): 

74 return '<%s value=%s errors=%s>' % ( 

75 self.__class__.__name__, self.value, self.errors 

76 ) 

77 

78 def as_form_field(self): 

79 value = '' if (self.value is None or self.value is False) else self.value 

80 return self.__class__(self._field, value, self.errors, self._prefix) 

81 

82 

83class JSONBoundField(BoundField): 

84 def as_form_field(self): 

85 value = self.value 

86 # When HTML form input is used and the input is not valid 

87 # value will be a JSONString, rather than a JSON primitive. 

88 if not getattr(value, 'is_json_string', False): 

89 try: 

90 value = json.dumps( 

91 self.value, 

92 sort_keys=True, 

93 indent=4, 

94 separators=(',', ': '), 

95 ) 

96 except (TypeError, ValueError): 

97 pass 

98 return self.__class__(self._field, value, self.errors, self._prefix) 

99 

100 

101class NestedBoundField(BoundField): 

102 """ 

103 This `BoundField` additionally implements __iter__ and __getitem__ 

104 in order to support nested bound fields. This class is the type of 

105 `BoundField` that is used for serializer fields. 

106 """ 

107 

108 def __init__(self, field, value, errors, prefix=''): 

109 if value is None or value == '' or not isinstance(value, Mapping): 

110 value = {} 

111 super().__init__(field, value, errors, prefix) 

112 

113 def __iter__(self): 

114 for field in self.fields.values(): 

115 yield self[field.field_name] 

116 

117 def __getitem__(self, key): 

118 field = self.fields[key] 

119 value = self.value.get(key) if self.value else None 

120 error = self.errors.get(key) if isinstance(self.errors, dict) else None 

121 if hasattr(field, 'fields'): 

122 return NestedBoundField(field, value, error, prefix=self.name + '.') 

123 elif getattr(field, '_is_jsonfield', False): 

124 return JSONBoundField(field, value, error, prefix=self.name + '.') 

125 return BoundField(field, value, error, prefix=self.name + '.') 

126 

127 def as_form_field(self): 

128 values = {} 

129 for key, value in self.value.items(): 

130 if isinstance(value, (list, dict)): 

131 values[key] = value 

132 else: 

133 values[key] = '' if (value is None or value is False) else force_str(value) 

134 return self.__class__(self._field, values, self.errors, self._prefix) 

135 

136 

137class BindingDict(MutableMapping): 

138 """ 

139 This dict-like object is used to store fields on a serializer. 

140 

141 This ensures that whenever fields are added to the serializer we call 

142 `field.bind()` so that the `field_name` and `parent` attributes 

143 can be set correctly. 

144 """ 

145 

146 def __init__(self, serializer): 

147 self.serializer = serializer 

148 self.fields = OrderedDict() 

149 

150 def __setitem__(self, key, field): 

151 self.fields[key] = field 

152 field.bind(field_name=key, parent=self.serializer) 

153 

154 def __getitem__(self, key): 

155 return self.fields[key] 

156 

157 def __delitem__(self, key): 

158 del self.fields[key] 

159 

160 def __iter__(self): 

161 return iter(self.fields) 

162 

163 def __len__(self): 

164 return len(self.fields) 

165 

166 def __repr__(self): 

167 return dict.__repr__(self.fields)