diff -r b758351d191f -r cc9b7e14412b web/lib/django/core/serializers/xml_serializer.py --- a/web/lib/django/core/serializers/xml_serializer.py Wed May 19 17:43:59 2010 +0200 +++ b/web/lib/django/core/serializers/xml_serializer.py Tue May 25 02:43:45 2010 +0200 @@ -4,7 +4,7 @@ from django.conf import settings from django.core.serializers import base -from django.db import models +from django.db import models, DEFAULT_DB_ALIAS from django.utils.xmlutils import SimplerXMLGenerator from django.utils.encoding import smart_unicode from xml.dom import pulldom @@ -81,13 +81,22 @@ self._start_relational_field(field) related = getattr(obj, field.name) if related is not None: - if field.rel.field_name == related._meta.pk.name: - # Related to remote object via primary key - related = related._get_pk_val() + if self.use_natural_keys and hasattr(related, 'natural_key'): + # If related object has a natural key, use it + related = related.natural_key() + # Iterable natural keys are rolled out as subelements + for key_value in related: + self.xml.startElement("natural", {}) + self.xml.characters(smart_unicode(key_value)) + self.xml.endElement("natural") else: - # Related to remote object via other field - related = getattr(related, field.rel.field_name) - self.xml.characters(smart_unicode(related)) + if field.rel.field_name == related._meta.pk.name: + # Related to remote object via primary key + related = related._get_pk_val() + else: + # Related to remote object via other field + related = getattr(related, field.rel.field_name) + self.xml.characters(smart_unicode(related)) else: self.xml.addQuickElement("None") self.xml.endElement("field") @@ -98,10 +107,27 @@ serialized as references to the object's PK (i.e. the related *data* is not dumped, just the relation). """ - if field.creates_table: + if field.rel.through._meta.auto_created: self._start_relational_field(field) + if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): + # If the objects in the m2m have a natural key, use it + def handle_m2m(value): + natural = value.natural_key() + # Iterable natural keys are rolled out as subelements + self.xml.startElement("object", {}) + for key_value in natural: + self.xml.startElement("natural", {}) + self.xml.characters(smart_unicode(key_value)) + self.xml.endElement("natural") + self.xml.endElement("object") + else: + def handle_m2m(value): + self.xml.addQuickElement("object", attrs={ + 'pk' : smart_unicode(value._get_pk_val()) + }) for relobj in getattr(obj, field.name).iterator(): - self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())}) + handle_m2m(relobj) + self.xml.endElement("field") def _start_relational_field(self, field): @@ -123,6 +149,7 @@ def __init__(self, stream_or_string, **options): super(Deserializer, self).__init__(stream_or_string, **options) self.event_stream = pulldom.parse(self.stream) + self.db = options.pop('using', DEFAULT_DB_ALIAS) def next(self): for event, node in self.event_stream: @@ -187,16 +214,44 @@ if node.getElementsByTagName('None'): return None else: - return field.rel.to._meta.get_field(field.rel.field_name).to_python( - getInnerText(node).strip()) + if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): + keys = node.getElementsByTagName('natural') + if keys: + # If there are 'natural' subelements, it must be a natural key + field_value = [getInnerText(k).strip() for k in keys] + obj = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value) + obj_pk = getattr(obj, field.rel.field_name) + # If this is a natural foreign key to an object that + # has a FK/O2O as the foreign key, use the FK value + if field.rel.to._meta.pk.rel: + obj_pk = obj_pk.pk + else: + # Otherwise, treat like a normal PK + field_value = getInnerText(node).strip() + obj_pk = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) + return obj_pk + else: + field_value = getInnerText(node).strip() + return field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) def _handle_m2m_field_node(self, node, field): """ Handle a node for a ManyToManyField. """ - return [field.rel.to._meta.pk.to_python( - c.getAttribute("pk")) - for c in node.getElementsByTagName("object")] + if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): + def m2m_convert(n): + keys = n.getElementsByTagName('natural') + if keys: + # If there are 'natural' subelements, it must be a natural key + field_value = [getInnerText(k).strip() for k in keys] + obj_pk = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk + else: + # Otherwise, treat like a normal PK value. + obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk')) + return obj_pk + else: + m2m_convert = lambda n: field.rel.to._meta.pk.to_python(n.getAttribute('pk')) + return [m2m_convert(c) for c in node.getElementsByTagName("object")] def _get_model_from_node(self, node, attr): """ @@ -233,4 +288,3 @@ else: pass return u"".join(inner_text) -