Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/core/serializers/python.py: 21%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

126 statements  

1""" 

2A Python "serializer". Doesn't do much serializing per se -- just converts to 

3and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for 

4other serializers. 

5""" 

6 

7from django.apps import apps 

8from django.core.serializers import base 

9from django.db import DEFAULT_DB_ALIAS, models 

10from django.db.models import CompositePrimaryKey 

11from django.utils.encoding import is_protected_type 

12 

13 

14class Serializer(base.Serializer): 

15 """ 

16 Serialize a QuerySet to basic Python objects. 

17 """ 

18 

19 internal_use_only = True 

20 

21 def start_serialization(self): 

22 self._current = None 

23 self.objects = [] 

24 

25 def end_serialization(self): 

26 pass 

27 

28 def start_object(self, obj): 

29 self._current = {} 

30 

31 def end_object(self, obj): 

32 self.objects.append(self.get_dump_object(obj)) 

33 self._current = None 

34 

35 def get_dump_object(self, obj): 

36 data = {"model": str(obj._meta)} 

37 if not self.use_natural_primary_keys or not hasattr(obj, "natural_key"): 

38 data["pk"] = self._value_from_field(obj, obj._meta.pk) 

39 data["fields"] = self._current 

40 return data 

41 

42 def _value_from_field(self, obj, field): 

43 if isinstance(field, CompositePrimaryKey): 

44 return [self._value_from_field(obj, f) for f in field] 

45 value = field.value_from_object(obj) 

46 # Protected types (i.e., primitives like None, numbers, dates, 

47 # and Decimals) are passed through as is. All other values are 

48 # converted to string first. 

49 return value if is_protected_type(value) else field.value_to_string(obj) 

50 

51 def handle_field(self, obj, field): 

52 self._current[field.name] = self._value_from_field(obj, field) 

53 

54 def handle_fk_field(self, obj, field): 

55 if self.use_natural_foreign_keys and hasattr( 

56 field.remote_field.model, "natural_key" 

57 ): 

58 related = getattr(obj, field.name) 

59 if related: 

60 value = related.natural_key() 

61 else: 

62 value = None 

63 else: 

64 value = self._value_from_field(obj, field) 

65 self._current[field.name] = value 

66 

67 def handle_m2m_field(self, obj, field): 

68 if field.remote_field.through._meta.auto_created: 

69 if self.use_natural_foreign_keys and hasattr( 

70 field.remote_field.model, "natural_key" 

71 ): 

72 

73 def m2m_value(value): 

74 return value.natural_key() 

75 

76 def queryset_iterator(obj, field): 

77 attr = getattr(obj, field.name) 

78 chunk_size = ( 

79 2000 if getattr(attr, "prefetch_cache_name", None) else None 

80 ) 

81 return attr.iterator(chunk_size) 

82 

83 else: 

84 

85 def m2m_value(value): 

86 return self._value_from_field(value, value._meta.pk) 

87 

88 def queryset_iterator(obj, field): 

89 query_set = getattr(obj, field.name).select_related(None).only("pk") 

90 chunk_size = 2000 if query_set._prefetch_related_lookups else None 

91 return query_set.iterator(chunk_size=chunk_size) 

92 

93 m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get( 

94 field.name, 

95 queryset_iterator(obj, field), 

96 ) 

97 self._current[field.name] = [m2m_value(related) for related in m2m_iter] 

98 

99 def getvalue(self): 

100 return self.objects 

101 

102 

103class Deserializer(base.Deserializer): 

104 """ 

105 Deserialize simple Python objects back into Django ORM instances. 

106 

107 It's expected that you pass the Python objects themselves (instead of a 

108 stream or a string) to the constructor 

109 """ 

110 

111 def __init__( 

112 self, object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options 

113 ): 

114 super().__init__(object_list, **options) 

115 self.handle_forward_references = options.pop("handle_forward_references", False) 

116 self.using = using 

117 self.ignorenonexistent = ignorenonexistent 

118 self.field_names_cache = {} # Model: <list of field_names> 

119 self._iterator = None 

120 

121 def __iter__(self): 

122 for obj in self.stream: 

123 yield from self._handle_object(obj) 

124 

125 def __next__(self): 

126 if self._iterator is None: 

127 self._iterator = iter(self) 

128 return next(self._iterator) 

129 

130 def _handle_object(self, obj): 

131 data = {} 

132 m2m_data = {} 

133 deferred_fields = {} 

134 

135 # Look up the model and starting build a dict of data for it. 

136 try: 

137 Model = self._get_model_from_node(obj["model"]) 

138 except base.DeserializationError: 

139 if self.ignorenonexistent: 

140 return 

141 raise 

142 if "pk" in obj: 

143 try: 

144 data[Model._meta.pk.attname] = Model._meta.pk.to_python(obj.get("pk")) 

145 except Exception as e: 

146 raise base.DeserializationError.WithData( 

147 e, obj["model"], obj.get("pk"), None 

148 ) 

149 

150 if Model not in self.field_names_cache: 

151 self.field_names_cache[Model] = {f.name for f in Model._meta.get_fields()} 

152 field_names = self.field_names_cache[Model] 

153 

154 # Handle each field 

155 for field_name, field_value in obj["fields"].items(): 

156 if self.ignorenonexistent and field_name not in field_names: 

157 # skip fields no longer on model 

158 continue 

159 

160 field = Model._meta.get_field(field_name) 

161 

162 # Handle M2M relations 

163 if field.remote_field and isinstance( 

164 field.remote_field, models.ManyToManyRel 

165 ): 

166 try: 

167 values = self._handle_m2m_field_node(field, field_value) 

168 if values == base.DEFER_FIELD: 

169 deferred_fields[field] = field_value 

170 else: 

171 m2m_data[field.name] = values 

172 except base.M2MDeserializationError as e: 

173 raise base.DeserializationError.WithData( 

174 e.original_exc, obj["model"], obj.get("pk"), e.pk 

175 ) 

176 

177 # Handle FK fields 

178 elif field.remote_field and isinstance( 

179 field.remote_field, models.ManyToOneRel 

180 ): 

181 try: 

182 value = self._handle_fk_field_node(field, field_value) 

183 if value == base.DEFER_FIELD: 

184 deferred_fields[field] = field_value 

185 else: 

186 data[field.attname] = value 

187 except Exception as e: 

188 raise base.DeserializationError.WithData( 

189 e, obj["model"], obj.get("pk"), field_value 

190 ) 

191 

192 # Handle all other fields 

193 else: 

194 try: 

195 data[field.name] = field.to_python(field_value) 

196 except Exception as e: 

197 raise base.DeserializationError.WithData( 

198 e, obj["model"], obj.get("pk"), field_value 

199 ) 

200 

201 model_instance = base.build_instance(Model, data, self.using) 

202 yield base.DeserializedObject(model_instance, m2m_data, deferred_fields) 

203 

204 def _handle_m2m_field_node(self, field, field_value): 

205 return base.deserialize_m2m_values( 

206 field, field_value, self.using, self.handle_forward_references 

207 ) 

208 

209 def _handle_fk_field_node(self, field, field_value): 

210 return base.deserialize_fk_value( 

211 field, field_value, self.using, self.handle_forward_references 

212 ) 

213 

214 @staticmethod 

215 def _get_model_from_node(model_identifier): 

216 """Look up a model from an "app_label.model_name" string.""" 

217 try: 

218 return apps.get_model(model_identifier) 

219 except (LookupError, TypeError): 

220 raise base.DeserializationError( 

221 f"Invalid model identifier: {model_identifier}" 

222 )