| 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 A Python "serializer", based on the default Django python serializer. | 18 A Python "serializer", based on the default Django python serializer. |
| 19 | 19 |
| 20 The only customisation is in the deserialization process which needs to take | 20 The only customisation is in the deserialization process which needs to take |
| 21 special care to resolve the name and parent attributes of the key for each | 21 special care to resolve the name and parent attributes of the key for each |
| 22 entity and also recreate the keys for any references appropriately. | 22 entity and also recreate the keys for any references appropriately. |
| 23 """ | 23 """ |
| 24 | 24 |
| 25 import datetime | |
| 26 import time | |
| 25 | 27 |
| 26 from django.conf import settings | 28 from django.conf import settings |
| 27 from django.core.serializers import base | 29 from django.core.serializers import base |
| 28 from django.core.serializers import python | 30 from django.core.serializers import python |
| 29 from django.db import models | 31 from django.db import models |
| 30 | 32 |
| 31 from google.appengine.api import datastore_types | 33 from google.appengine.api import datastore_types |
| 32 from google.appengine.ext import db | 34 from google.appengine.ext import db |
| 33 | 35 |
| 34 try: | 36 try: |
| 35 from django.utils.encoding import smart_unicode | 37 from django.utils.encoding import smart_unicode |
| 36 except ImportError: | 38 except ImportError: |
| 37 import datetime | |
| 38 import types | 39 import types |
| 39 from django.utils.functional import Promise | 40 from django.utils.functional import Promise |
| 40 | 41 |
| 41 def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): | 42 def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): |
| 42 """ | 43 """ |
| 43 Similar to smart_unicode, except that lazy instances are resolved to | 44 Similar to smart_unicode, except that lazy instances are resolved to |
| 44 strings, rather than kept as lazy objects. | 45 strings, rather than kept as lazy objects. |
| 45 | 46 |
| 46 If strings_only is True, don't convert (some) non-string-like objects. | 47 If strings_only is True, don't convert (some) non-string-like objects. |
| 47 """ | 48 """ |
| 48 non_strings = (types.NoneType, int, long, datetime.datetime, datetime.date, | 49 non_strings = (types.NoneType, int, long, datetime.datetime, datetime.date, |
| 49 datetime.time, float) | 50 datetime.time, float) |
| 50 if strings_only and isinstance(s, non_strings): | 51 if strings_only and isinstance(s, non_strings): |
| 51 return s | 52 return s |
| 52 if not isinstance(s, basestring,): | 53 if not isinstance(s, basestring,): |
| 53 if hasattr(s, '__unicode__'): | 54 if hasattr(s, '__unicode__'): |
| 54 s = unicode(s) | 55 s = unicode(s) |
| 55 else: | 56 else: |
| 56 s = unicode(str(s), encoding, errors) | 57 s = unicode(str(s), encoding, errors) |
| 57 elif not isinstance(s, unicode): | 58 elif not isinstance(s, unicode): |
| 58 # Note: We use .decode() here, instead of unicode(s, encoding, | 59 # Note: We use .decode() here, instead of unicode(s, encoding, |
| 59 # errors), so that if s is a SafeString, it ends up being a | 60 # errors), so that if s is a SafeString, it ends up being a |
| 60 # SafeUnicode at the end. | 61 # SafeUnicode at the end. |
| 61 s = s.decode(encoding, errors) | 62 s = s.decode(encoding, errors) |
| 62 return s | 63 return s |
| 63 | 64 |
| 64 def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): | 65 def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): |
| 65 """ | 66 """ |
| 66 Returns a unicode object representing 's'. Treats bytestrings using the | 67 Returns a unicode object representing 's'. Treats bytestrings using the |
| 67 'encoding' codec. | 68 'encoding' codec. |
| 68 | 69 |
| 69 If strings_only is True, don't convert (some) non-string-like objects. | 70 If strings_only is True, don't convert (some) non-string-like objects. |
| 70 """ | 71 """ |
| 71 if isinstance(s, Promise): | 72 if isinstance(s, Promise): |
| 72 # The input is the result of a gettext_lazy() call. | 73 # The input is the result of a gettext_lazy() call. |
| 73 return s | 74 return s |
| 74 return force_unicode(s, encoding, strings_only, errors) | 75 return force_unicode(s, encoding, strings_only, errors) |
| 75 | 76 |
| 76 Serializer = python.Serializer | 77 Serializer = python.Serializer |
| 78 | |
| 79 DATE_FORMAT = "%Y-%m-%d" | |
| 80 TIME_FORMAT = "%H:%M:%S" | |
|
GvR
2008/05/13 16:40:54
I think it makes more sense to define a single con
| |
| 77 | 81 |
| 78 | 82 |
| 79 class FakeParent(object): | 83 class FakeParent(object): |
| 80 """Fake parent 'model' like object. | 84 """Fake parent 'model' like object. |
| 81 | 85 |
| 82 This class exists to allow a parent object to be provided to a new model | 86 This class exists to allow a parent object to be provided to a new model |
| 83 without having to load the parent instance itself. | 87 without having to load the parent instance itself. |
| 84 """ | 88 """ |
| 85 | 89 |
| 86 def __init__(self, parent_key): | 90 def __init__(self, parent_key): |
| 87 self._entity = parent_key | 91 self._entity = parent_key |
| 88 | 92 |
| 89 | 93 |
| 90 def Deserializer(object_list, **options): | 94 def Deserializer(object_list, **options): |
| 91 """Deserialize simple Python objects back into Model instances. | 95 """Deserialize simple Python objects back into Model instances. |
| 92 | 96 |
| 93 It's expected that you pass the Python objects themselves (instead of a | 97 It's expected that you pass the Python objects themselves (instead of a |
| 94 stream or a string) to the constructor | 98 stream or a string) to the constructor |
| 95 """ | 99 """ |
| 96 models.get_apps() | 100 models.get_apps() |
| 97 for d in object_list: | 101 for d in object_list: |
| 98 # Look up the model and starting build a dict of data for it. | 102 # Look up the model and starting build a dict of data for it. |
| 99 Model = python._get_model(d["model"]) | 103 Model = python._get_model(d["model"]) |
| 100 data = {} | 104 data = {} |
| 101 key = resolve_key(Model._meta.module_name, d["pk"]) | 105 key = resolve_key(Model._meta.module_name, d["pk"]) |
| 102 if key.name(): | 106 if key.name(): |
| 103 data["key_name"] = key.name() | 107 data["key_name"] = key.name() |
| 104 parent = None | 108 parent = None |
| 105 if key.parent(): | 109 if key.parent(): |
| 106 parent = FakeParent(key.parent()) | 110 parent = FakeParent(key.parent()) |
| 107 m2m_data = {} | 111 m2m_data = {} |
| 108 | 112 |
| 109 # Handle each field | 113 # Handle each field |
| 110 for (field_name, field_value) in d["fields"].iteritems(): | 114 for (field_name, field_value) in d["fields"].iteritems(): |
| 111 if isinstance(field_value, str): | 115 if isinstance(field_value, str): |
| 112 field_value = smart_unicode( | 116 field_value = smart_unicode( |
| 113 field_value, options.get("encoding", | 117 field_value, options.get("encoding", |
| 114 settings.DEFAULT_CHARSET), | 118 settings.DEFAULT_CHARSET), |
| 115 strings_only=True) | 119 strings_only=True) |
| 116 field = Model.properties()[field_name] | 120 field = Model.properties()[field_name] |
| 117 | 121 |
| 118 if isinstance(field, db.Reference): | 122 if isinstance(field, db.Reference): |
|
GvR
2008/05/15 18:32:28
PS. Should use db.ReferenceProperty here -- db.Ref
| |
| 119 # Resolve foreign key references. | 123 # Resolve foreign key references. |
| 120 data[field.name] = resolve_key(Model._meta.module_name, field_value) | 124 data[field.name] = resolve_key(Model._meta.module_name, field_value) |
| 121 if not data[field.name].name(): | 125 if not data[field.name].name(): |
| 122 raise base.DeserializationError(u"Cannot load Reference with " | 126 raise base.DeserializationError(u"Cannot load Reference with " |
| 123 "unnamed key: '%s'" % field_value) | 127 "unnamed key: '%s'" % field_value) |
| 124 else: | 128 continue |
| 125 data[field.name] = field.validate(field_value) | 129 |
| 130 if isinstance(field, db.DateTimeProperty): | |
|
GvR
2008/05/13 16:40:54
I'm reluctant to see a growing list of
if isins
| |
| 131 try: | |
| 132 t = time.strptime(field_value, "%s %s"% (DATE_FORMAT, TIME_FORMAT)) | |
| 133 d = datetime.datetime(*(t)[0:6]) | |
| 134 field_value = d | |
|
GvR
2008/05/13 16:40:54
Any reason you aren't writing
field_value = dat
| |
| 135 except (ValueError, TypeError), e: | |
|
GvR
2008/05/13 16:40:54
I'm uncomfortable casting such a wide net. I'd mu
| |
| 136 # Pass it on anyway, and hope the model can deal with it. | |
| 137 pass | |
| 138 | |
| 139 data[field.name] = field.validate(field_value) | |
| 126 # Create the new model instance with all it's data, but no parent. | 140 # Create the new model instance with all it's data, but no parent. |
| 127 object = Model(**data) | 141 object = Model(**data) |
| 128 # Now add the parent into the hidden attribute, bypassing the type checks | 142 # Now add the parent into the hidden attribute, bypassing the type checks |
| 129 # in the Model's __init__ routine. | 143 # in the Model's __init__ routine. |
| 130 object._parent = parent | 144 object._parent = parent |
| 131 # When the deserialized object is saved our replacement DeserializedObject | 145 # When the deserialized object is saved our replacement DeserializedObject |
| 132 # class will set object._parent to force the real parent model to be loaded | 146 # class will set object._parent to force the real parent model to be loaded |
| 133 # the first time it is referenced. | 147 # the first time it is referenced. |
| 134 yield base.DeserializedObject(object, m2m_data) | 148 yield base.DeserializedObject(object, m2m_data) |
| 135 | 149 |
| 136 | 150 |
| 137 def resolve_key(model, key_data): | 151 def resolve_key(model, key_data): |
| 138 """Creates a Key instance from a some data. | 152 """Creates a Key instance from a some data. |
| 139 | 153 |
| 140 Args: | 154 Args: |
| 141 model: The name of the model this key is being resolved for. Only used in | 155 model: The name of the model this key is being resolved for. Only used in |
| 142 the fourth case below (a plain key_name string). | 156 the fourth case below (a plain key_name string). |
| 143 key_data: The data to create a key instance from. May be in four formats: | 157 key_data: The data to create a key instance from. May be in four formats: |
| 144 * The str() output of a key instance. Eg. A base64 encoded string. | 158 * The str() output of a key instance. Eg. A base64 encoded string. |
| 145 * The repr() output of a key instance. Eg. A string for eval(). | 159 * The repr() output of a key instance. Eg. A string for eval(). |
| 146 * A list of arguments to pass to db.Key.from_path. | 160 * A list of arguments to pass to db.Key.from_path. |
| 147 * A single string value, being the key_name of the instance. When this | 161 * A single string value, being the key_name of the instance. When this |
| 148 format is used the resulting key has no parent, and is for the model | 162 format is used the resulting key has no parent, and is for the model |
| 149 named in the model parameter. | 163 named in the model parameter. |
| 150 | 164 |
| 151 Returns: | 165 Returns: |
| 152 An instance of db.Key. If the data cannot be used to create a Key instance | 166 An instance of db.Key. If the data cannot be used to create a Key instance |
| 153 an error will be raised. | 167 an error will be raised. |
| 154 """ | 168 """ |
| 155 if isinstance(key_data, list): | 169 if isinstance(key_data, list): |
| 156 # The key_data is a from_path sequence. | 170 # The key_data is a from_path sequence. |
| 157 return db.Key.from_path(*key_data) | 171 return db.Key.from_path(*key_data) |
| 158 elif isinstance(key_data, basestring): | 172 elif isinstance(key_data, basestring): |
| 159 if key_data.find("from_path") != -1: | 173 if key_data.find("from_path") != -1: |
| 160 # key_data is encoded in repr(key) format | 174 # key_data is encoded in repr(key) format |
| 161 return eval(key_data) | 175 return eval(key_data) |
| 162 else: | 176 else: |
| 163 try: | 177 try: |
| 164 # key_data encoded a str(key) format | 178 # key_data encoded a str(key) format |
| 165 return db.Key(key_data) | 179 return db.Key(key_data) |
| 166 except datastore_types.datastore_errors.BadKeyError, e: | 180 except datastore_types.datastore_errors.BadKeyError, e: |
| 167 # Final try, assume it's a plain key name for the model. | 181 # Final try, assume it's a plain key name for the model. |
| 168 return db.Key.from_path(model, key_data) | 182 return db.Key.from_path(model, key_data) |
| 169 else: | 183 else: |
| 170 raise base.DeserializationError(u"Invalid key data: '%s'" % key_data) | 184 raise base.DeserializationError(u"Invalid key data: '%s'" % key_data) |
| OLD | NEW |