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