diff options
Diffstat (limited to 'appengine_django/serializer/python.py')
-rwxr-xr-x | appengine_django/serializer/python.py | 130 |
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) |