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
« 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
4from django.core.exceptions import ImproperlyConfigured
5from django.db import models
6from rest_framework import serializers
7from rest_framework.fields import empty
10class PolymorphicSerializer(serializers.Serializer):
11 model_serializer_mapping = None
12 resource_type_field_name = 'resourcetype'
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)
30 def __init__(self, *args, **kwargs):
31 super(PolymorphicSerializer, self).__init__(*args, **kwargs)
33 model_serializer_mapping = self.model_serializer_mapping
34 self.model_serializer_mapping = {}
35 self.resource_type_model_mapping = {}
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
43 self.resource_type_model_mapping[resource_type] = model
44 self.model_serializer_mapping[model] = serializer
46 # ----------
47 # Public API
49 def to_resource_type(self, model_or_instance):
50 return model_or_instance._meta.object_name
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)
60 ret = serializer.to_representation(instance)
61 ret[self.resource_type_field_name] = resource_type
62 return ret
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)
72 ret = serializer.to_internal_value(data)
73 ret[self.resource_type_field_name] = resource_type
74 return ret
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)
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)
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)
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
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)
111 validated_data = serializer.run_validation(data)
112 validated_data[self.resource_type_field_name] = resource_type
113 return validated_data
115 # --------------
116 # Implementation
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)
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 })
131 def _get_serializer_from_model_or_instance(self, model_or_instance):
132 model = self._to_model(model_or_instance)
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]
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 )
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 })
156 return self._get_serializer_from_model_or_instance(model)