summaryrefslogtreecommitdiffstats
path: root/appengine_django/serializer/python.py
diff options
context:
space:
mode:
Diffstat (limited to 'appengine_django/serializer/python.py')
-rwxr-xr-xappengine_django/serializer/python.py130
1 files changed, 130 insertions, 0 deletions
diff --git a/appengine_django/serializer/python.py b/appengine_django/serializer/python.py
new file mode 100755
index 0000000..bce16e7
--- /dev/null
+++ b/appengine_django/serializer/python.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+A Python "serializer", based on the default Django python serializer.
+
+The only customisation is in the deserialization process which needs to take
+special care to resolve the name and parent attributes of the key for each
+entity and also recreate the keys for any references appropriately.
+"""
+
+
+from django.conf import settings
+from django.core.serializers import base
+from django.core.serializers import python
+from django.db import models
+
+from google.appengine.api import datastore_types
+from google.appengine.ext import db
+
+from django.utils.encoding import smart_unicode
+
+Serializer = python.Serializer
+
+
+class FakeParent(object):
+ """Fake parent 'model' like object.
+
+ This class exists to allow a parent object to be provided to a new model
+ without having to load the parent instance itself.
+ """
+
+ def __init__(self, parent_key):
+ self._entity = parent_key
+
+
+def Deserializer(object_list, **options):
+ """Deserialize simple Python objects back into Model instances.
+
+ It's expected that you pass the Python objects themselves (instead of a
+ stream or a string) to the constructor
+ """
+ models.get_apps()
+ for d in object_list:
+ # Look up the model and starting build a dict of data for it.
+ Model = python._get_model(d["model"])
+ data = {}
+ key = resolve_key(Model._meta.module_name, d["pk"])
+ if key.name():
+ data["key_name"] = key.name()
+ parent = None
+ if key.parent():
+ parent = FakeParent(key.parent())
+ m2m_data = {}
+
+ # Handle each field
+ for (field_name, field_value) in d["fields"].iteritems():
+ if isinstance(field_value, str):
+ field_value = smart_unicode(
+ field_value, options.get("encoding",
+ settings.DEFAULT_CHARSET),
+ strings_only=True)
+ field = Model.properties()[field_name]
+
+ if isinstance(field, db.Reference):
+ # Resolve foreign key references.
+ data[field.name] = resolve_key(Model._meta.module_name, field_value)
+ if not data[field.name].name():
+ raise base.DeserializationError(u"Cannot load Reference with "
+ "unnamed key: '%s'" % field_value)
+ else:
+ data[field.name] = field.validate(field_value)
+ # Create the new model instance with all it's data, but no parent.
+ object = Model(**data)
+ # Now add the parent into the hidden attribute, bypassing the type checks
+ # in the Model's __init__ routine.
+ object._parent = parent
+ # When the deserialized object is saved our replacement DeserializedObject
+ # class will set object._parent to force the real parent model to be loaded
+ # the first time it is referenced.
+ yield base.DeserializedObject(object, m2m_data)
+
+
+def resolve_key(model, key_data):
+ """Creates a Key instance from a some data.
+
+ Args:
+ model: The name of the model this key is being resolved for. Only used in
+ the fourth case below (a plain key_name string).
+ key_data: The data to create a key instance from. May be in four formats:
+ * The str() output of a key instance. Eg. A base64 encoded string.
+ * The repr() output of a key instance. Eg. A string for eval().
+ * A list of arguments to pass to db.Key.from_path.
+ * A single string value, being the key_name of the instance. When this
+ format is used the resulting key has no parent, and is for the model
+ named in the model parameter.
+
+ Returns:
+ An instance of db.Key. If the data cannot be used to create a Key instance
+ an error will be raised.
+ """
+ if isinstance(key_data, list):
+ # The key_data is a from_path sequence.
+ return db.Key.from_path(*key_data)
+ elif isinstance(key_data, basestring):
+ if key_data.find("from_path") != -1:
+ # key_data is encoded in repr(key) format
+ return eval(key_data)
+ else:
+ try:
+ # key_data encoded a str(key) format
+ return db.Key(key_data)
+ except datastore_types.datastore_errors.BadKeyError, e:
+ # Final try, assume it's a plain key name for the model.
+ return db.Key.from_path(model, key_data)
+ else:
+ raise base.DeserializationError(u"Invalid key data: '%s'" % key_data)