/appengine_django/serializer/python.py
http://google-app-engine-django.googlecode.com/ · Python · 170 lines · 135 code · 9 blank · 26 comment · 4 complexity · 6c039e81c3d7fd49605702fbac756163 MD5 · raw file
- #!/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.
- """
- 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
- """
- 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)
- else:
- # 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,
- '%H:%M:%S').time()
- 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.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 parse_datetime_with_microseconds(field_value, format):
- """Parses a string to a datetime object including microseconds.
- Args:
- 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.
- Returns:
- A datetime instance.
- """
- try:
- # 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]+)$',
- str(e))
- if not match:
- raise
- ms_str = match.group(1)
- 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.
- 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)