Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/core/serializers/__init__.py: 13%

106 statements  

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

1""" 

2Interfaces for serializing Django objects. 

3 

4Usage:: 

5 

6 from django.core import serializers 

7 json = serializers.serialize("json", some_queryset) 

8 objects = list(serializers.deserialize("json", json)) 

9 

10To add your own serializers, use the SERIALIZATION_MODULES setting:: 

11 

12 SERIALIZATION_MODULES = { 

13 "csv": "path.to.csv.serializer", 

14 "txt": "path.to.txt.serializer", 

15 } 

16 

17""" 

18 

19import importlib 

20 

21from django.apps import apps 

22from django.conf import settings 

23from django.core.serializers.base import SerializerDoesNotExist 

24 

25# Built-in serializers 

26BUILTIN_SERIALIZERS = { 

27 "xml": "django.core.serializers.xml_serializer", 

28 "python": "django.core.serializers.python", 

29 "json": "django.core.serializers.json", 

30 "yaml": "django.core.serializers.pyyaml", 

31 "jsonl": "django.core.serializers.jsonl", 

32} 

33 

34_serializers = {} 

35 

36 

37class BadSerializer: 

38 """ 

39 Stub serializer to hold exception raised during registration 

40 

41 This allows the serializer registration to cache serializers and if there 

42 is an error raised in the process of creating a serializer it will be 

43 raised and passed along to the caller when the serializer is used. 

44 """ 

45 

46 internal_use_only = False 

47 

48 def __init__(self, exception): 

49 self.exception = exception 

50 

51 def __call__(self, *args, **kwargs): 

52 raise self.exception 

53 

54 

55def register_serializer(format, serializer_module, serializers=None): 

56 """Register a new serializer. 

57 

58 ``serializer_module`` should be the fully qualified module name 

59 for the serializer. 

60 

61 If ``serializers`` is provided, the registration will be added 

62 to the provided dictionary. 

63 

64 If ``serializers`` is not provided, the registration will be made 

65 directly into the global register of serializers. Adding serializers 

66 directly is not a thread-safe operation. 

67 """ 

68 if serializers is None and not _serializers: 

69 _load_serializers() 

70 

71 try: 

72 module = importlib.import_module(serializer_module) 

73 except ImportError as exc: 

74 bad_serializer = BadSerializer(exc) 

75 

76 module = type( 

77 "BadSerializerModule", 

78 (), 

79 { 

80 "Deserializer": bad_serializer, 

81 "Serializer": bad_serializer, 

82 }, 

83 ) 

84 

85 if serializers is None: 

86 _serializers[format] = module 

87 else: 

88 serializers[format] = module 

89 

90 

91def unregister_serializer(format): 

92 "Unregister a given serializer. This is not a thread-safe operation." 

93 if not _serializers: 

94 _load_serializers() 

95 if format not in _serializers: 

96 raise SerializerDoesNotExist(format) 

97 del _serializers[format] 

98 

99 

100def get_serializer(format): 

101 if not _serializers: 

102 _load_serializers() 

103 if format not in _serializers: 

104 raise SerializerDoesNotExist(format) 

105 return _serializers[format].Serializer 

106 

107 

108def get_serializer_formats(): 

109 if not _serializers: 

110 _load_serializers() 

111 return list(_serializers) 

112 

113 

114def get_public_serializer_formats(): 

115 if not _serializers: 

116 _load_serializers() 

117 return [k for k, v in _serializers.items() if not v.Serializer.internal_use_only] 

118 

119 

120def get_deserializer(format): 

121 if not _serializers: 

122 _load_serializers() 

123 if format not in _serializers: 

124 raise SerializerDoesNotExist(format) 

125 return _serializers[format].Deserializer 

126 

127 

128def serialize(format, queryset, **options): 

129 """ 

130 Serialize a queryset (or any iterator that returns database objects) using 

131 a certain serializer. 

132 """ 

133 s = get_serializer(format)() 

134 s.serialize(queryset, **options) 

135 return s.getvalue() 

136 

137 

138def deserialize(format, stream_or_string, **options): 

139 """ 

140 Deserialize a stream or a string. Return an iterator that yields ``(obj, 

141 m2m_relation_dict)``, where ``obj`` is an instantiated -- but *unsaved* -- 

142 object, and ``m2m_relation_dict`` is a dictionary of ``{m2m_field_name : 

143 list_of_related_objects}``. 

144 """ 

145 d = get_deserializer(format) 

146 return d(stream_or_string, **options) 

147 

148 

149def _load_serializers(): 

150 """ 

151 Register built-in and settings-defined serializers. This is done lazily so 

152 that user code has a chance to (e.g.) set up custom settings without 

153 needing to be careful of import order. 

154 """ 

155 global _serializers 

156 serializers = {} 

157 for format in BUILTIN_SERIALIZERS: 

158 register_serializer(format, BUILTIN_SERIALIZERS[format], serializers) 

159 if hasattr(settings, "SERIALIZATION_MODULES"): 

160 for format in settings.SERIALIZATION_MODULES: 

161 register_serializer( 

162 format, settings.SERIALIZATION_MODULES[format], serializers 

163 ) 

164 _serializers = serializers 

165 

166 

167def sort_dependencies(app_list, allow_cycles=False): 

168 """Sort a list of (app_config, models) pairs into a single list of models. 

169 

170 The single list of models is sorted so that any model with a natural key 

171 is serialized before a normal model, and any model with a natural key 

172 dependency has it's dependencies serialized first. 

173 

174 If allow_cycles is True, return the best-effort ordering that will respect 

175 most of dependencies but ignore some of them to break the cycles. 

176 """ 

177 # Process the list of models, and get the list of dependencies 

178 model_dependencies = [] 

179 models = set() 

180 for app_config, model_list in app_list: 

181 if model_list is None: 

182 model_list = app_config.get_models() 

183 

184 for model in model_list: 

185 models.add(model) 

186 # Add any explicitly defined dependencies 

187 if hasattr(model, "natural_key"): 

188 deps = getattr(model.natural_key, "dependencies", []) 

189 if deps: 

190 deps = [apps.get_model(dep) for dep in deps] 

191 else: 

192 deps = [] 

193 

194 # Now add a dependency for any FK relation with a model that 

195 # defines a natural key 

196 for field in model._meta.fields: 

197 if field.remote_field: 

198 rel_model = field.remote_field.model 

199 if hasattr(rel_model, "natural_key") and rel_model != model: 

200 deps.append(rel_model) 

201 # Also add a dependency for any simple M2M relation with a model 

202 # that defines a natural key. M2M relations with explicit through 

203 # models don't count as dependencies. 

204 for field in model._meta.many_to_many: 

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

206 rel_model = field.remote_field.model 

207 if hasattr(rel_model, "natural_key") and rel_model != model: 

208 deps.append(rel_model) 

209 model_dependencies.append((model, deps)) 

210 

211 model_dependencies.reverse() 

212 # Now sort the models to ensure that dependencies are met. This 

213 # is done by repeatedly iterating over the input list of models. 

214 # If all the dependencies of a given model are in the final list, 

215 # that model is promoted to the end of the final list. This process 

216 # continues until the input list is empty, or we do a full iteration 

217 # over the input models without promoting a model to the final list. 

218 # If we do a full iteration without a promotion, that means there are 

219 # circular dependencies in the list. 

220 model_list = [] 

221 while model_dependencies: 

222 skipped = [] 

223 changed = False 

224 while model_dependencies: 

225 model, deps = model_dependencies.pop() 

226 

227 # If all of the models in the dependency list are either already 

228 # on the final model list, or not on the original serialization list, 

229 # then we've found another model with all it's dependencies satisfied. 

230 if all(d not in models or d in model_list for d in deps): 

231 model_list.append(model) 

232 changed = True 

233 else: 

234 skipped.append((model, deps)) 

235 if not changed: 

236 if allow_cycles: 

237 # If cycles are allowed, add the last skipped model and ignore 

238 # its dependencies. This could be improved by some graph 

239 # analysis to ignore as few dependencies as possible. 

240 model, _ = skipped.pop() 

241 model_list.append(model) 

242 else: 

243 raise RuntimeError( 

244 "Can't resolve dependencies for %s in serialized app list." 

245 % ", ".join( 

246 model._meta.label 

247 for model, deps in sorted( 

248 skipped, key=lambda obj: obj[0].__name__ 

249 ) 

250 ), 

251 ) 

252 model_dependencies = skipped 

253 

254 return model_list