|
1 """ |
|
2 A Python "serializer". Doesn't do much serializing per se -- just converts to |
|
3 and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for |
|
4 other serializers. |
|
5 """ |
|
6 |
|
7 from django.conf import settings |
|
8 from django.core.serializers import base |
|
9 from django.db import models |
|
10 from django.utils.encoding import smart_unicode |
|
11 |
|
12 class Serializer(base.Serializer): |
|
13 """ |
|
14 Serializes a QuerySet to basic Python objects. |
|
15 """ |
|
16 |
|
17 internal_use_only = True |
|
18 |
|
19 def start_serialization(self): |
|
20 self._current = None |
|
21 self.objects = [] |
|
22 |
|
23 def end_serialization(self): |
|
24 pass |
|
25 |
|
26 def start_object(self, obj): |
|
27 self._current = {} |
|
28 |
|
29 def end_object(self, obj): |
|
30 self.objects.append({ |
|
31 "model" : smart_unicode(obj._meta), |
|
32 "pk" : smart_unicode(obj._get_pk_val(), strings_only=True), |
|
33 "fields" : self._current |
|
34 }) |
|
35 self._current = None |
|
36 |
|
37 def handle_field(self, obj, field): |
|
38 self._current[field.name] = smart_unicode(getattr(obj, field.name), strings_only=True) |
|
39 |
|
40 def handle_fk_field(self, obj, field): |
|
41 related = getattr(obj, field.name) |
|
42 if related is not None: |
|
43 if field.rel.field_name == related._meta.pk.name: |
|
44 # Related to remote object via primary key |
|
45 related = related._get_pk_val() |
|
46 else: |
|
47 # Related to remote object via other field |
|
48 related = getattr(related, field.rel.field_name) |
|
49 self._current[field.name] = smart_unicode(related, strings_only=True) |
|
50 |
|
51 def handle_m2m_field(self, obj, field): |
|
52 self._current[field.name] = [smart_unicode(related._get_pk_val(), strings_only=True) |
|
53 for related in getattr(obj, field.name).iterator()] |
|
54 |
|
55 def getvalue(self): |
|
56 return self.objects |
|
57 |
|
58 def Deserializer(object_list, **options): |
|
59 """ |
|
60 Deserialize simple Python objects back into Django ORM instances. |
|
61 |
|
62 It's expected that you pass the Python objects themselves (instead of a |
|
63 stream or a string) to the constructor |
|
64 """ |
|
65 models.get_apps() |
|
66 for d in object_list: |
|
67 # Look up the model and starting build a dict of data for it. |
|
68 Model = _get_model(d["model"]) |
|
69 data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])} |
|
70 m2m_data = {} |
|
71 |
|
72 # Handle each field |
|
73 for (field_name, field_value) in d["fields"].iteritems(): |
|
74 if isinstance(field_value, str): |
|
75 field_value = smart_unicode(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True) |
|
76 |
|
77 field = Model._meta.get_field(field_name) |
|
78 |
|
79 # Handle M2M relations |
|
80 if field.rel and isinstance(field.rel, models.ManyToManyRel): |
|
81 m2m_convert = field.rel.to._meta.pk.to_python |
|
82 m2m_data[field.name] = [m2m_convert(smart_unicode(pk)) for pk in field_value] |
|
83 |
|
84 # Handle FK fields |
|
85 elif field.rel and isinstance(field.rel, models.ManyToOneRel): |
|
86 if field_value: |
|
87 data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) |
|
88 else: |
|
89 data[field.attname] = None |
|
90 |
|
91 # Handle all other fields |
|
92 else: |
|
93 data[field.name] = field.to_python(field_value) |
|
94 |
|
95 yield base.DeserializedObject(Model(**data), m2m_data) |
|
96 |
|
97 def _get_model(model_identifier): |
|
98 """ |
|
99 Helper to look up a model from an "app_label.module_name" string. |
|
100 """ |
|
101 try: |
|
102 Model = models.get_model(*model_identifier.split(".")) |
|
103 except TypeError: |
|
104 Model = None |
|
105 if Model is None: |
|
106 raise base.DeserializationError(u"Invalid model identifier: '%s'" % model_identifier) |
|
107 return Model |