|
1 #!/usr/bin/python2.5 |
|
2 # |
|
3 # Copyright 2008 the Melange authors. |
|
4 # |
|
5 # Licensed under the Apache License, Version 2.0 (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 |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 # Unless required by applicable law or agreed to in writing, software |
|
12 # distributed under the License is distributed on an "AS IS" BASIS, |
|
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 # See the License for the specific language governing permissions and |
|
15 # limitations under the License. |
|
16 |
|
17 """Helpers functions for updating different kinds of models in datastore. |
|
18 """ |
|
19 |
|
20 __authors__ = [ |
|
21 '"Todd Larsen" <tlarsen@google.com>', |
|
22 '"Sverre Rabbelier" <sverer@rabbelier.nl>', |
|
23 '"Pawel Solyga" <pawel.solyga@gmail.com>', |
|
24 ] |
|
25 |
|
26 |
|
27 from google.appengine.ext import db |
|
28 |
|
29 from soc.logic import key_name |
|
30 from soc.logic import out_of_band |
|
31 |
|
32 |
|
33 class Logic(): |
|
34 """Base logic for entity classes. |
|
35 |
|
36 The BaseLogic class functions specific to Entity classes by relying |
|
37 on the the child-classes to implement _model, _name and _key_name |
|
38 """ |
|
39 |
|
40 def _updateField(self, model, name, value): |
|
41 """Hook called when a field is updated. |
|
42 |
|
43 Base classes should override if any special actions need to be |
|
44 taken when a field is updated. The field is not updated if the |
|
45 method does not return a True value. |
|
46 """ |
|
47 |
|
48 return True |
|
49 |
|
50 def getFromKeyName(self, key_name): |
|
51 """"Returns User entity for key_name or None if not found. |
|
52 - |
|
53 - Args: |
|
54 - key_name: key name of entity |
|
55 """ |
|
56 |
|
57 return self._model.get_by_key_name(key_name) |
|
58 |
|
59 def getFromFields(self, **kwargs): |
|
60 """Returns the entity for a given link name, or None if not found. |
|
61 |
|
62 Args: |
|
63 **kwargs: the fields of the entity that uniquely identifies it |
|
64 """ |
|
65 |
|
66 key_name = self.getKeyNameForFields(**kwargs) |
|
67 |
|
68 if key_name: |
|
69 entity = self._model.get_by_key_name(key_name) |
|
70 else: |
|
71 entity = None |
|
72 |
|
73 return entity |
|
74 |
|
75 def getIfFields(self, **kwargs): |
|
76 """Returns entity for supplied link name if one exists. |
|
77 |
|
78 Args: |
|
79 **kwargs: the fields of the entity that uniquely identifies it |
|
80 |
|
81 Returns: |
|
82 * None if a field is false. |
|
83 * Eentity for supplied fields |
|
84 |
|
85 Raises: |
|
86 out_of_band.ErrorResponse if link name is not false, but no Sponsor entity |
|
87 with the supplied link name exists in the Datastore |
|
88 """ |
|
89 |
|
90 if not all(kwargs.values()): |
|
91 # exit without error, to let view know that link_name was not supplied |
|
92 return None |
|
93 |
|
94 entity = self.getFromFields(**kwargs) |
|
95 |
|
96 if entity: |
|
97 # a Sponsor exist for this link_name, so return that Sponsor entity |
|
98 return entity |
|
99 |
|
100 fields = [] |
|
101 |
|
102 for key, value in kwargs.iteritems(): |
|
103 fields.extend('"%s" is "%s" ' % (key, value)) |
|
104 |
|
105 # else: fields were supplied, but there is no Entity that has it |
|
106 raise out_of_band.ErrorResponse( |
|
107 'There is no %s with %s.' % (self._name, ''.join(fields)), status=404) |
|
108 |
|
109 def getKeyNameForFields(self, **kwargs): |
|
110 """Return a Datastore key_name for a Entity from the specified fields. |
|
111 |
|
112 Args: |
|
113 **kwargs: the fields of the entity that uniquely identifies it |
|
114 """ |
|
115 |
|
116 if not all(kwargs.values()): |
|
117 return None |
|
118 |
|
119 return self._keyName(**kwargs) |
|
120 |
|
121 def getForLimitAndOffset(self, limit, offset=0): |
|
122 """Returns entities for given offset and limit or None if not found. |
|
123 |
|
124 Args: |
|
125 limit: max amount of entities to return |
|
126 offset: optional offset in entities list which defines first entity to |
|
127 return; default is zero (first entity) |
|
128 """ |
|
129 |
|
130 query = self._model.all() |
|
131 return query.fetch(limit, offset) |
|
132 |
|
133 def updateModelProperties(self, model, **model_properties): |
|
134 """Update existing model entity using supplied model properties. |
|
135 |
|
136 Args: |
|
137 model: a model entity |
|
138 **model_properties: keyword arguments that correspond to model entity |
|
139 properties and their values |
|
140 |
|
141 Returns: |
|
142 the original model entity with any supplied properties changed |
|
143 """ |
|
144 |
|
145 def update(): |
|
146 return self._unsafeUpdateModelProperties(model, **model_properties) |
|
147 |
|
148 return db.run_in_transaction(update) |
|
149 |
|
150 def _unsafeUpdateModelProperties(self, model, **model_properties): |
|
151 """(see updateModelProperties) |
|
152 |
|
153 Like updateModelProperties(), but not run within a transaction. |
|
154 """ |
|
155 |
|
156 properties = model.properties() |
|
157 |
|
158 for prop in properties.values(): |
|
159 name = prop.name |
|
160 |
|
161 if not name in self._skip_properties and name in model_properties: |
|
162 value = model_properties[prop.name] |
|
163 |
|
164 if self._updateField(model, name, value): |
|
165 prop.__set__(model, value) |
|
166 |
|
167 model.put() |
|
168 return model |
|
169 |
|
170 def updateOrCreateFromKeyName(self, properties, key_name): |
|
171 """Update existing entity, or create new one with supplied properties. |
|
172 |
|
173 Args: |
|
174 properties: dictionairy with entity properties and their values |
|
175 key_name: the key_name of the entity that uniquely identifies it |
|
176 |
|
177 Returns: |
|
178 the entity corresponding to the key_name, with any supplied |
|
179 properties changed, or a new entity now associated with the |
|
180 supplied key_name and properties. |
|
181 """ |
|
182 |
|
183 entity = self.getFromKeyName(key_name) |
|
184 |
|
185 if not entity: |
|
186 # entity did not exist, so create one in a transaction |
|
187 entity = self._model.get_or_insert(key_name, **properties) |
|
188 |
|
189 # there is no way to be sure if get_or_insert() returned a new entity or |
|
190 # got an existing one due to a race, so update with sponsor_properties anyway, |
|
191 # in a transaction |
|
192 return self.updateModelProperties(entity, **properties) |
|
193 |
|
194 def updateOrCreateFromFields(self, properties, **kwargs): |
|
195 """Like updateOrCreateFromKeyName, but resolves **kwargs to a key_name first |
|
196 """ |
|
197 |
|
198 # attempt to retrieve the existing entity |
|
199 key_name = self.getKeyNameForFields(**kwargs) |
|
200 |
|
201 return self.updateOrCreateFromKeyName(properties, key_name) |
|
202 |
|
203 def delete(self, entity): |
|
204 """Delete existing entity from datastore. |
|
205 |
|
206 Args: |
|
207 entity: an existing entity in datastore |
|
208 """ |
|
209 |
|
210 entity.delete() |