|
0
|
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 |
|
29
|
9 |
from django.db import models, DEFAULT_DB_ALIAS |
|
0
|
10 |
from django.utils.encoding import smart_unicode, is_protected_type |
|
|
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 |
value = field._get_val_from_obj(obj) |
|
|
39 |
# Protected types (i.e., primitives like None, numbers, dates, |
|
|
40 |
# and Decimals) are passed through as is. All other values are |
|
|
41 |
# converted to string first. |
|
|
42 |
if is_protected_type(value): |
|
|
43 |
self._current[field.name] = value |
|
|
44 |
else: |
|
|
45 |
self._current[field.name] = field.value_to_string(obj) |
|
|
46 |
|
|
|
47 |
def handle_fk_field(self, obj, field): |
|
|
48 |
related = getattr(obj, field.name) |
|
|
49 |
if related is not None: |
|
29
|
50 |
if self.use_natural_keys and hasattr(related, 'natural_key'): |
|
|
51 |
related = related.natural_key() |
|
0
|
52 |
else: |
|
29
|
53 |
if field.rel.field_name == related._meta.pk.name: |
|
|
54 |
# Related to remote object via primary key |
|
|
55 |
related = related._get_pk_val() |
|
|
56 |
else: |
|
|
57 |
# Related to remote object via other field |
|
|
58 |
related = smart_unicode(getattr(related, field.rel.field_name), strings_only=True) |
|
|
59 |
self._current[field.name] = related |
|
0
|
60 |
|
|
|
61 |
def handle_m2m_field(self, obj, field): |
|
29
|
62 |
if field.rel.through._meta.auto_created: |
|
|
63 |
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): |
|
|
64 |
m2m_value = lambda value: value.natural_key() |
|
|
65 |
else: |
|
|
66 |
m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True) |
|
|
67 |
self._current[field.name] = [m2m_value(related) |
|
0
|
68 |
for related in getattr(obj, field.name).iterator()] |
|
|
69 |
|
|
|
70 |
def getvalue(self): |
|
|
71 |
return self.objects |
|
|
72 |
|
|
|
73 |
def Deserializer(object_list, **options): |
|
|
74 |
""" |
|
|
75 |
Deserialize simple Python objects back into Django ORM instances. |
|
|
76 |
|
|
|
77 |
It's expected that you pass the Python objects themselves (instead of a |
|
|
78 |
stream or a string) to the constructor |
|
|
79 |
""" |
|
29
|
80 |
db = options.pop('using', DEFAULT_DB_ALIAS) |
|
0
|
81 |
models.get_apps() |
|
|
82 |
for d in object_list: |
|
|
83 |
# Look up the model and starting build a dict of data for it. |
|
|
84 |
Model = _get_model(d["model"]) |
|
|
85 |
data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])} |
|
|
86 |
m2m_data = {} |
|
|
87 |
|
|
|
88 |
# Handle each field |
|
|
89 |
for (field_name, field_value) in d["fields"].iteritems(): |
|
|
90 |
if isinstance(field_value, str): |
|
|
91 |
field_value = smart_unicode(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True) |
|
|
92 |
|
|
|
93 |
field = Model._meta.get_field(field_name) |
|
|
94 |
|
|
|
95 |
# Handle M2M relations |
|
|
96 |
if field.rel and isinstance(field.rel, models.ManyToManyRel): |
|
29
|
97 |
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): |
|
|
98 |
def m2m_convert(value): |
|
|
99 |
if hasattr(value, '__iter__'): |
|
|
100 |
return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk |
|
|
101 |
else: |
|
|
102 |
return smart_unicode(field.rel.to._meta.pk.to_python(value)) |
|
|
103 |
else: |
|
|
104 |
m2m_convert = lambda v: smart_unicode(field.rel.to._meta.pk.to_python(v)) |
|
|
105 |
m2m_data[field.name] = [m2m_convert(pk) for pk in field_value] |
|
0
|
106 |
|
|
|
107 |
# Handle FK fields |
|
|
108 |
elif field.rel and isinstance(field.rel, models.ManyToOneRel): |
|
|
109 |
if field_value is not None: |
|
29
|
110 |
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): |
|
|
111 |
if hasattr(field_value, '__iter__'): |
|
|
112 |
obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value) |
|
|
113 |
value = getattr(obj, field.rel.field_name) |
|
|
114 |
# If this is a natural foreign key to an object that |
|
|
115 |
# has a FK/O2O as the foreign key, use the FK value |
|
|
116 |
if field.rel.to._meta.pk.rel: |
|
|
117 |
value = value.pk |
|
|
118 |
else: |
|
|
119 |
value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) |
|
|
120 |
data[field.attname] = value |
|
|
121 |
else: |
|
|
122 |
data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) |
|
0
|
123 |
else: |
|
|
124 |
data[field.attname] = None |
|
|
125 |
|
|
|
126 |
# Handle all other fields |
|
|
127 |
else: |
|
|
128 |
data[field.name] = field.to_python(field_value) |
|
|
129 |
|
|
|
130 |
yield base.DeserializedObject(Model(**data), m2m_data) |
|
|
131 |
|
|
|
132 |
def _get_model(model_identifier): |
|
|
133 |
""" |
|
|
134 |
Helper to look up a model from an "app_label.module_name" string. |
|
|
135 |
""" |
|
|
136 |
try: |
|
|
137 |
Model = models.get_model(*model_identifier.split(".")) |
|
|
138 |
except TypeError: |
|
|
139 |
Model = None |
|
|
140 |
if Model is None: |
|
|
141 |
raise base.DeserializationError(u"Invalid model identifier: '%s'" % model_identifier) |
|
|
142 |
return Model |