Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_polymorphic/serializers.py: 70%

96 statements  

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

1from collections.abc import Mapping 

2from six import string_types 

3 

4from django.core.exceptions import ImproperlyConfigured 

5from django.db import models 

6from rest_framework import serializers 

7from rest_framework.fields import empty 

8 

9 

10class PolymorphicSerializer(serializers.Serializer): 

11 model_serializer_mapping = None 

12 resource_type_field_name = 'resourcetype' 

13 

14 def __new__(cls, *args, **kwargs): 

15 if cls.model_serializer_mapping is None: 15 ↛ 16line 15 didn't jump to line 16, because the condition on line 15 was never true

16 raise ImproperlyConfigured( 

17 '`{cls}` is missing a ' 

18 '`{cls}.model_serializer_mapping` attribute'.format( 

19 cls=cls.__name__ 

20 ) 

21 ) 

22 if not isinstance(cls.resource_type_field_name, string_types): 22 ↛ 23line 22 didn't jump to line 23, because the condition on line 22 was never true

23 raise ImproperlyConfigured( 

24 '`{cls}.resource_type_field_name` must be a string'.format( 

25 cls=cls.__name__ 

26 ) 

27 ) 

28 return super(PolymorphicSerializer, cls).__new__(cls, *args, **kwargs) 

29 

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

31 super(PolymorphicSerializer, self).__init__(*args, **kwargs) 

32 

33 model_serializer_mapping = self.model_serializer_mapping 

34 self.model_serializer_mapping = {} 

35 self.resource_type_model_mapping = {} 

36 

37 for model, serializer in model_serializer_mapping.items(): 

38 resource_type = self.to_resource_type(model) 

39 if callable(serializer): 39 ↛ 43line 39 didn't jump to line 43, because the condition on line 39 was never false

40 serializer = serializer(*args, **kwargs) 

41 serializer.parent = self 

42 

43 self.resource_type_model_mapping[resource_type] = model 

44 self.model_serializer_mapping[model] = serializer 

45 

46 # ---------- 

47 # Public API 

48 

49 def to_resource_type(self, model_or_instance): 

50 return model_or_instance._meta.object_name 

51 

52 def to_representation(self, instance): 

53 if isinstance(instance, Mapping): 53 ↛ 54line 53 didn't jump to line 54, because the condition on line 53 was never true

54 resource_type = self._get_resource_type_from_mapping(instance) 

55 serializer = self._get_serializer_from_resource_type(resource_type) 

56 else: 

57 resource_type = self.to_resource_type(instance) 

58 serializer = self._get_serializer_from_model_or_instance(instance) 

59 

60 ret = serializer.to_representation(instance) 

61 ret[self.resource_type_field_name] = resource_type 

62 return ret 

63 

64 def to_internal_value(self, data): 

65 if self.partial and self.instance: 

66 resource_type = self.to_resource_type(self.instance) 

67 serializer = self._get_serializer_from_model_or_instance(self.instance) 

68 else: 

69 resource_type = self._get_resource_type_from_mapping(data) 

70 serializer = self._get_serializer_from_resource_type(resource_type) 

71 

72 ret = serializer.to_internal_value(data) 

73 ret[self.resource_type_field_name] = resource_type 

74 return ret 

75 

76 def create(self, validated_data): 

77 resource_type = validated_data.pop(self.resource_type_field_name) 

78 serializer = self._get_serializer_from_resource_type(resource_type) 

79 return serializer.create(validated_data) 

80 

81 def update(self, instance, validated_data): 

82 resource_type = validated_data.pop(self.resource_type_field_name) 

83 serializer = self._get_serializer_from_resource_type(resource_type) 

84 return serializer.update(instance, validated_data) 

85 

86 def is_valid(self, *args, **kwargs): 

87 valid = super(PolymorphicSerializer, self).is_valid(*args, **kwargs) 

88 try: 

89 if self.partial and self.instance: 89 ↛ 90line 89 didn't jump to line 90, because the condition on line 89 was never true

90 resource_type = self.to_resource_type(self.instance) 

91 serializer = self._get_serializer_from_model_or_instance(self.instance) 

92 else: 

93 resource_type = self._get_resource_type_from_mapping(self.initial_data) 

94 serializer = self._get_serializer_from_resource_type(resource_type) 

95 

96 except serializers.ValidationError: 

97 child_valid = False 

98 else: 

99 child_valid = serializer.is_valid(*args, **kwargs) 

100 self._errors.update(serializer.errors) 

101 return valid and child_valid 

102 

103 def run_validation(self, data=empty): 

104 if self.partial and self.instance: 104 ↛ 105line 104 didn't jump to line 105, because the condition on line 104 was never true

105 resource_type = self.to_resource_type(self.instance) 

106 serializer = self._get_serializer_from_model_or_instance(self.instance) 

107 else: 

108 resource_type = self._get_resource_type_from_mapping(data) 

109 serializer = self._get_serializer_from_resource_type(resource_type) 

110 

111 validated_data = serializer.run_validation(data) 

112 validated_data[self.resource_type_field_name] = resource_type 

113 return validated_data 

114 

115 # -------------- 

116 # Implementation 

117 

118 def _to_model(self, model_or_instance): 

119 return (model_or_instance.__class__ 

120 if isinstance(model_or_instance, models.Model) 

121 else model_or_instance) 

122 

123 def _get_resource_type_from_mapping(self, mapping): 

124 try: 

125 return mapping[self.resource_type_field_name] 

126 except KeyError: 

127 raise serializers.ValidationError({ 

128 self.resource_type_field_name: 'This field is required', 

129 }) 

130 

131 def _get_serializer_from_model_or_instance(self, model_or_instance): 

132 model = self._to_model(model_or_instance) 

133 

134 for klass in model.mro(): 134 ↛ 138line 134 didn't jump to line 138, because the loop on line 134 didn't complete

135 if klass in self.model_serializer_mapping: 

136 return self.model_serializer_mapping[klass] 

137 

138 raise KeyError( 

139 '`{cls}.model_serializer_mapping` is missing ' 

140 'a corresponding serializer for `{model}` model'.format( 

141 cls=self.__class__.__name__, 

142 model=model.__name__ 

143 ) 

144 ) 

145 

146 def _get_serializer_from_resource_type(self, resource_type): 

147 try: 

148 model = self.resource_type_model_mapping[resource_type] 

149 except KeyError: 

150 raise serializers.ValidationError({ 

151 self.resource_type_field_name: 'Invalid {0}'.format( 

152 self.resource_type_field_name 

153 ) 

154 }) 

155 

156 return self._get_serializer_from_model_or_instance(model)