Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Tree: 07b0b2f18a
Fetching contributors…

Cannot retrieve contributors at this time

171 lines (147 sloc) 6.425 kB
# 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
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.
import datetime
import re
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
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"])
data["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",
field =[field_name]
if isinstance(field, db.Reference):
# Resolve foreign key references.
data[] = resolve_key(Model._meta.module_name, field_value)
# Handle converting strings to more specific formats.
if isinstance(field_value, basestring):
if isinstance(field, db.DateProperty):
field_value = datetime.datetime.strptime(
field_value, '%Y-%m-%d').date()
elif isinstance(field, db.TimeProperty):
field_value = parse_datetime_with_microseconds(field_value,
elif isinstance(field, db.DateTimeProperty):
field_value = parse_datetime_with_microseconds(field_value,
'%Y-%m-%d %H:%M:%S')
# Handle pyyaml datetime.time deserialization - it returns a datetime
# instead of a time.
if (isinstance(field_value, datetime.datetime) and
isinstance(field, db.TimeProperty)):
field_value = field_value.time()
data[] = 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 parse_datetime_with_microseconds(field_value, format):
"""Parses a string to a datetime object including microseconds.
field_value: The string to parse.
format: The format string to parse to datetime.strptime. Not including a
format specifier for the expected microseconds component.
A datetime instance.
# This will only return if no microseconds were availanle.
return datetime.datetime.strptime(field_value, format)
except ValueError, e:
# Hack to deal with microseconds.
match = re.match(r'unconverted data remains: \.([0-9]+)$',
if not match:
ms_str =
without_ms = field_value[:-(len(ms_str)+1)]
new_value = datetime.datetime.strptime(without_ms, format)
return new_value.replace(microsecond=int(ms_str))
def resolve_key(model, key_data):
"""Creates a Key instance from a some data.
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.
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)
# 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)
raise base.DeserializationError(u"Invalid key data: '%s'" % key_data)
Jump to Line
Something went wrong with that request. Please try again.