PageRenderTime 49ms CodeModel.GetById 16ms app.highlight 24ms RepoModel.GetById 2ms app.codeStats 0ms

/appengine_django/serializer/xml.py

http://google-app-engine-django.googlecode.com/
Python | 157 lines | 113 code | 14 blank | 30 comment | 0 complexity | e1f12c024228941b952068f90f915c70 MD5 | raw file
  1#!/usr/bin/python2.4
  2#
  3# Copyright 2008 Google Inc.
  4#
  5# Licensed under the Apache License, Version 2.0 (the "License");
  6# you may not use this file except in compliance with the License.
  7# You may obtain a copy of the License at
  8#
  9#     http://www.apache.org/licenses/LICENSE-2.0
 10#
 11# Unless required by applicable law or agreed to in writing, software
 12# distributed under the License is distributed on an "AS IS" BASIS,
 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14# See the License for the specific language governing permissions and
 15# limitations under the License.
 16
 17
 18"""
 19Replaces the default Django XML serializer with one that uses the built in
 20ToXml method for each entity.
 21"""
 22
 23from datetime import datetime
 24import re
 25
 26from django.conf import settings
 27from django.core.serializers import base
 28from django.core.serializers import xml_serializer
 29from django.db import models
 30
 31from google.appengine.api import datastore_types
 32from google.appengine.ext import db
 33
 34from python import FakeParent
 35from python import parse_datetime_with_microseconds
 36
 37getInnerText = xml_serializer.getInnerText
 38
 39
 40class Serializer(xml_serializer.Serializer):
 41  """A Django Serializer class to convert datastore models to XML.
 42
 43  This class relies on the ToXml method of the entity behind each model to do
 44  the hard work.
 45  """
 46
 47  def __init__(self, *args, **kwargs):
 48    super(Serializer, self).__init__(*args, **kwargs)
 49    self._objects = []
 50
 51  def handle_field(self, obj, field):
 52    """Fields are not handled individually."""
 53    pass
 54
 55  def handle_fk_field(self, obj, field):
 56    """Fields are not handled individually."""
 57    pass
 58
 59  def start_object(self, obj):
 60    """Nothing needs to be done to start an object."""
 61    pass
 62
 63  def end_object(self, obj):
 64    """Serialize the object to XML and add to the list of objects to output.
 65
 66    The output of ToXml is manipulated to replace the datastore model name in
 67    the "kind" tag with the Django model name (which includes the Django
 68    application name) to make importing easier.
 69    """
 70    xml = obj._entity.ToXml()
 71    xml = xml.replace(u"""kind="%s" """ % obj._entity.kind(),
 72                      u"""kind="%s" """ % unicode(obj._meta))
 73    self._objects.append(xml)
 74
 75  def getvalue(self):
 76    """Wrap the serialized objects with XML headers and return."""
 77    str = u"""<?xml version="1.0" encoding="utf-8"?>\n"""
 78    str += u"""<django-objects version="1.0">\n"""
 79    str += u"".join(self._objects)
 80    str += u"""</django-objects>"""
 81    return str
 82
 83
 84class Deserializer(xml_serializer.Deserializer):
 85  """A Django Deserializer class to convert XML to Django objects.
 86
 87  This is a fairly manualy and simplistic XML parser, it supports just enough
 88  functionality to read the keys and fields for an entity from the XML file and
 89  construct a model object.
 90  """
 91
 92  def next(self):
 93    """Replacement next method to look for 'entity'.
 94
 95    The default next implementation exepects 'object' nodes which is not
 96    what the entity's ToXml output provides.
 97    """
 98    for event, node in self.event_stream:
 99      if event == "START_ELEMENT" and node.nodeName == "entity":
100        self.event_stream.expandNode(node)
101        return self._handle_object(node)
102    raise StopIteration
103
104  def _handle_object(self, node):
105    """Convert an <entity> node to a DeserializedObject"""
106    Model = self._get_model_from_node(node, "kind")
107    data = {}
108    key = db.Key(node.getAttribute("key"))
109    if key.name():
110      data["key_name"] = key.name()
111    parent = None
112    if key.parent():
113      parent = FakeParent(key.parent())
114    m2m_data = {}
115
116    # Deseralize each field.
117    for field_node in node.getElementsByTagName("property"):
118      # If the field is missing the name attribute, bail (are you
119      # sensing a pattern here?)
120      field_name = field_node.getAttribute("name")
121      if not field_name:
122          raise base.DeserializationError("<field> node is missing the 'name' "
123                                          "attribute")
124      field = Model.properties()[field_name]
125      field_value = getInnerText(field_node).strip()
126
127      if isinstance(field, db.Reference):
128        m = re.match("tag:.*\[(.*)\]", field_value)
129        if not m:
130          raise base.DeserializationError(u"Invalid reference value: '%s'" %
131                                          field_value)
132        key = m.group(1)
133        key_obj = db.Key(key)
134        if not key_obj.name():
135          raise base.DeserializationError(u"Cannot load Reference with "
136                                          "unnamed key: '%s'" % field_value)
137        data[field.name] = key_obj
138      else:
139        format = '%Y-%m-%d %H:%M:%S'
140        if isinstance(field, db.DateProperty):
141          field_value = datetime.strptime(field_value, format).date()
142        elif isinstance(field, db.TimeProperty):
143          field_value = parse_datetime_with_microseconds(field_value,
144                                                         format).time()
145        elif isinstance(field, db.DateTimeProperty):
146          field_value = parse_datetime_with_microseconds(field_value, format)
147        data[field.name] = field.validate(field_value)
148
149    # Create the new model instance with all it's data, but no parent.
150    object = Model(**data)
151    # Now add the parent into the hidden attribute, bypassing the type checks
152    # in the Model's __init__ routine.
153    object._parent = parent
154    # When the deserialized object is saved our replacement DeserializedObject
155    # class will set object._parent to force the real parent model to be loaded
156    # the first time it is referenced.
157    return base.DeserializedObject(object, m2m_data)