web/lib/django/core/serializers/xml_serializer.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     2 XML serializer.
     2 XML serializer.
     3 """
     3 """
     4 
     4 
     5 from django.conf import settings
     5 from django.conf import settings
     6 from django.core.serializers import base
     6 from django.core.serializers import base
     7 from django.db import models
     7 from django.db import models, DEFAULT_DB_ALIAS
     8 from django.utils.xmlutils import SimplerXMLGenerator
     8 from django.utils.xmlutils import SimplerXMLGenerator
     9 from django.utils.encoding import smart_unicode
     9 from django.utils.encoding import smart_unicode
    10 from xml.dom import pulldom
    10 from xml.dom import pulldom
    11 
    11 
    12 class Serializer(base.Serializer):
    12 class Serializer(base.Serializer):
    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.
   231         elif child.nodeType == child.ELEMENT_NODE:
   286         elif child.nodeType == child.ELEMENT_NODE:
   232             inner_text.extend(getInnerText(child))
   287             inner_text.extend(getInnerText(child))
   233         else:
   288         else:
   234            pass
   289            pass
   235     return u"".join(inner_text)
   290     return u"".join(inner_text)
   236