79 differently from regular fields). |
79 differently from regular fields). |
80 """ |
80 """ |
81 self._start_relational_field(field) |
81 self._start_relational_field(field) |
82 related = getattr(obj, field.name) |
82 related = getattr(obj, field.name) |
83 if related is not None: |
83 if related is not None: |
84 if field.rel.field_name == related._meta.pk.name: |
84 if self.use_natural_keys and hasattr(related, 'natural_key'): |
85 # Related to remote object via primary key |
85 # If related object has a natural key, use it |
86 related = related._get_pk_val() |
86 related = related.natural_key() |
87 else: |
87 # Iterable natural keys are rolled out as subelements |
88 # Related to remote object via other field |
88 for key_value in related: |
89 related = getattr(related, field.rel.field_name) |
89 self.xml.startElement("natural", {}) |
90 self.xml.characters(smart_unicode(related)) |
90 self.xml.characters(smart_unicode(key_value)) |
|
91 self.xml.endElement("natural") |
|
92 else: |
|
93 if field.rel.field_name == related._meta.pk.name: |
|
94 # Related to remote object via primary key |
|
95 related = related._get_pk_val() |
|
96 else: |
|
97 # Related to remote object via other field |
|
98 related = getattr(related, field.rel.field_name) |
|
99 self.xml.characters(smart_unicode(related)) |
91 else: |
100 else: |
92 self.xml.addQuickElement("None") |
101 self.xml.addQuickElement("None") |
93 self.xml.endElement("field") |
102 self.xml.endElement("field") |
94 |
103 |
95 def handle_m2m_field(self, obj, field): |
104 def handle_m2m_field(self, obj, field): |
96 """ |
105 """ |
97 Called to handle a ManyToManyField. Related objects are only |
106 Called to handle a ManyToManyField. Related objects are only |
98 serialized as references to the object's PK (i.e. the related *data* |
107 serialized as references to the object's PK (i.e. the related *data* |
99 is not dumped, just the relation). |
108 is not dumped, just the relation). |
100 """ |
109 """ |
101 if field.creates_table: |
110 if field.rel.through._meta.auto_created: |
102 self._start_relational_field(field) |
111 self._start_relational_field(field) |
|
112 if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): |
|
113 # If the objects in the m2m have a natural key, use it |
|
114 def handle_m2m(value): |
|
115 natural = value.natural_key() |
|
116 # Iterable natural keys are rolled out as subelements |
|
117 self.xml.startElement("object", {}) |
|
118 for key_value in natural: |
|
119 self.xml.startElement("natural", {}) |
|
120 self.xml.characters(smart_unicode(key_value)) |
|
121 self.xml.endElement("natural") |
|
122 self.xml.endElement("object") |
|
123 else: |
|
124 def handle_m2m(value): |
|
125 self.xml.addQuickElement("object", attrs={ |
|
126 'pk' : smart_unicode(value._get_pk_val()) |
|
127 }) |
103 for relobj in getattr(obj, field.name).iterator(): |
128 for relobj in getattr(obj, field.name).iterator(): |
104 self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())}) |
129 handle_m2m(relobj) |
|
130 |
105 self.xml.endElement("field") |
131 self.xml.endElement("field") |
106 |
132 |
107 def _start_relational_field(self, field): |
133 def _start_relational_field(self, field): |
108 """ |
134 """ |
109 Helper to output the <field> element for relational fields |
135 Helper to output the <field> element for relational fields |
121 """ |
147 """ |
122 |
148 |
123 def __init__(self, stream_or_string, **options): |
149 def __init__(self, stream_or_string, **options): |
124 super(Deserializer, self).__init__(stream_or_string, **options) |
150 super(Deserializer, self).__init__(stream_or_string, **options) |
125 self.event_stream = pulldom.parse(self.stream) |
151 self.event_stream = pulldom.parse(self.stream) |
|
152 self.db = options.pop('using', DEFAULT_DB_ALIAS) |
126 |
153 |
127 def next(self): |
154 def next(self): |
128 for event, node in self.event_stream: |
155 for event, node in self.event_stream: |
129 if event == "START_ELEMENT" and node.nodeName == "object": |
156 if event == "START_ELEMENT" and node.nodeName == "object": |
130 self.event_stream.expandNode(node) |
157 self.event_stream.expandNode(node) |
185 """ |
212 """ |
186 # Check if there is a child node named 'None', returning None if so. |
213 # Check if there is a child node named 'None', returning None if so. |
187 if node.getElementsByTagName('None'): |
214 if node.getElementsByTagName('None'): |
188 return None |
215 return None |
189 else: |
216 else: |
190 return field.rel.to._meta.get_field(field.rel.field_name).to_python( |
217 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): |
191 getInnerText(node).strip()) |
218 keys = node.getElementsByTagName('natural') |
|
219 if keys: |
|
220 # If there are 'natural' subelements, it must be a natural key |
|
221 field_value = [getInnerText(k).strip() for k in keys] |
|
222 obj = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value) |
|
223 obj_pk = getattr(obj, field.rel.field_name) |
|
224 # If this is a natural foreign key to an object that |
|
225 # has a FK/O2O as the foreign key, use the FK value |
|
226 if field.rel.to._meta.pk.rel: |
|
227 obj_pk = obj_pk.pk |
|
228 else: |
|
229 # Otherwise, treat like a normal PK |
|
230 field_value = getInnerText(node).strip() |
|
231 obj_pk = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) |
|
232 return obj_pk |
|
233 else: |
|
234 field_value = getInnerText(node).strip() |
|
235 return field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) |
192 |
236 |
193 def _handle_m2m_field_node(self, node, field): |
237 def _handle_m2m_field_node(self, node, field): |
194 """ |
238 """ |
195 Handle a <field> node for a ManyToManyField. |
239 Handle a <field> node for a ManyToManyField. |
196 """ |
240 """ |
197 return [field.rel.to._meta.pk.to_python( |
241 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): |
198 c.getAttribute("pk")) |
242 def m2m_convert(n): |
199 for c in node.getElementsByTagName("object")] |
243 keys = n.getElementsByTagName('natural') |
|
244 if keys: |
|
245 # If there are 'natural' subelements, it must be a natural key |
|
246 field_value = [getInnerText(k).strip() for k in keys] |
|
247 obj_pk = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk |
|
248 else: |
|
249 # Otherwise, treat like a normal PK value. |
|
250 obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk')) |
|
251 return obj_pk |
|
252 else: |
|
253 m2m_convert = lambda n: field.rel.to._meta.pk.to_python(n.getAttribute('pk')) |
|
254 return [m2m_convert(c) for c in node.getElementsByTagName("object")] |
200 |
255 |
201 def _get_model_from_node(self, node, attr): |
256 def _get_model_from_node(self, node, attr): |
202 """ |
257 """ |
203 Helper to look up a model from a <object model=...> or a <field |
258 Helper to look up a model from a <object model=...> or a <field |
204 rel=... to=...> node. |
259 rel=... to=...> node. |