Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(19)

Side by Side Diff: appengine_django/serializer/python.py

Issue 950: Google App Engine Django - Deserialize db.DateTimeProperty SVN Base: http://google-app-engine-django.googlecode.com/svn/trunk/
Patch Set: Created 3 months, 4 weeks ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
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)
OLDNEW

Powered by Google App Engine
This is Rietveld r305