11 following differences: |
11 following differences: |
12 |
12 |
13 * all methods returning `google.appengine.ext.db.Model` instance(s) will return |
13 * all methods returning `google.appengine.ext.db.Model` instance(s) will return |
14 `cubicweb.goa.db.Model` instance instead (though you should see almost no |
14 `cubicweb.goa.db.Model` instance instead (though you should see almost no |
15 difference since those instances have the same api) |
15 difference since those instances have the same api) |
16 |
16 |
17 * class methods returning model instance take a `req` as first argument, unless |
17 * class methods returning model instance take a `req` as first argument, unless |
18 they are called through an instance, representing the current request |
18 they are called through an instance, representing the current request |
19 (accessible through `self.req` on almost all objects) |
19 (accessible through `self.req` on almost all objects) |
20 |
20 |
21 * XXX no instance.<modelname>_set attributes, use instance.reverse_<attr name> |
21 * XXX no instance.<modelname>_set attributes, use instance.reverse_<attr name> |
22 instead |
22 instead |
23 * XXX reference property always return a list of objects, not the instance |
23 * XXX reference property always return a list of objects, not the instance |
24 * XXX name/collection_name argument of properties constructor are ignored |
24 * XXX name/collection_name argument of properties constructor are ignored |
25 * XXX ListProperty |
25 * XXX ListProperty |
45 from google.appengine.api.datastore import NormalizeAndTypeCheck, RunInTransaction |
45 from google.appengine.api.datastore import NormalizeAndTypeCheck, RunInTransaction |
46 from google.appengine.api.datastore_types import Text, Blob |
46 from google.appengine.api.datastore_types import Text, Blob |
47 from google.appengine.api.datastore_errors import BadKeyError |
47 from google.appengine.api.datastore_errors import BadKeyError |
48 |
48 |
49 # XXX remove this dependancy |
49 # XXX remove this dependancy |
50 from google.appengine.ext import db |
50 from google.appengine.ext import db |
51 |
51 |
52 |
52 |
53 def rset_from_objs(req, objs, attrs=('eid',), rql=None, args=None): |
53 def rset_from_objs(req, objs, attrs=('eid',), rql=None, args=None): |
54 """return a ResultSet instance for list of objects""" |
54 """return a ResultSet instance for list of objects""" |
55 if objs is None: |
55 if objs is None: |
74 value = str(value) |
74 value = str(value) |
75 else: |
75 else: |
76 value = obj[attr] |
76 value = obj[attr] |
77 descr = str(eschema.destination(attr)) |
77 descr = str(eschema.destination(attr)) |
78 line.append(value) |
78 line.append(value) |
79 linedescr.append(descr) |
79 linedescr.append(descr) |
80 rows.append(line) |
80 rows.append(line) |
81 description.append(linedescr) |
81 description.append(linedescr) |
82 for j, attr in enumerate(attrs): |
82 for j, attr in enumerate(attrs): |
83 if attr == 'eid': |
83 if attr == 'eid': |
84 entity = vreg.etype_class(eschema.type)(req, rset, i, j) |
84 entity = vreg.etype_class(eschema.type)(req, rset, i, j) |
85 rset._get_entity_cache_ = {(i, j): entity} |
85 rset._get_entity_cache_ = {(i, j): entity} |
86 rset.rowcount = len(rows) |
86 rset.rowcount = len(rows) |
87 req.decorate_rset(rset) |
87 req.decorate_rset(rset) |
88 return rset |
88 return rset |
89 |
89 |
90 |
90 |
91 def needrequest(wrapped): |
91 def needrequest(wrapped): |
92 def wrapper(cls, *args, **kwargs): |
92 def wrapper(cls, *args, **kwargs): |
100 raise Exception('either call this method on an instance or ' |
100 raise Exception('either call this method on an instance or ' |
101 'specify the req argument') |
101 'specify the req argument') |
102 return wrapped(cls, req, *args, **kwargs) |
102 return wrapped(cls, req, *args, **kwargs) |
103 return iclassmethod(wrapper) |
103 return iclassmethod(wrapper) |
104 |
104 |
105 |
105 |
106 class gaedbmetaentity(metaentity): |
106 class gaedbmetaentity(metaentity): |
107 """metaclass for goa.db.Model classes: filter entity / db model part, |
107 """metaclass for goa.db.Model classes: filter entity / db model part, |
108 put aside the db model part for later creation of db model class. |
108 put aside the db model part for later creation of db model class. |
109 """ |
109 """ |
110 def __new__(mcs, name, bases, classdict): |
110 def __new__(mcs, name, bases, classdict): |
137 |
137 |
138 |
138 |
139 class Model(entities.AnyEntity): |
139 class Model(entities.AnyEntity): |
140 id = 'Any' |
140 id = 'Any' |
141 __metaclass__ = gaedbmetaentity |
141 __metaclass__ = gaedbmetaentity |
142 |
142 |
143 row = col = 0 |
143 row = col = 0 |
144 |
144 |
145 @classmethod |
145 @classmethod |
146 def __initialize__(cls): |
146 def __initialize__(cls): |
147 super(Model, cls).__initialize__() |
147 super(Model, cls).__initialize__() |
148 cls._attributes = frozenset(rschema for rschema in cls.e_schema.subject_relations() |
148 cls._attributes = frozenset(rschema for rschema in cls.e_schema.subject_relations() |
149 if rschema.is_final()) |
149 if rschema.is_final()) |
150 |
150 |
151 def __init__(self, *args, **kwargs): |
151 def __init__(self, *args, **kwargs): |
152 # db.Model prototype: |
152 # db.Model prototype: |
153 # __init__(self, parent=None, key_name=None, **kw) |
153 # __init__(self, parent=None, key_name=None, **kw) |
154 # |
154 # |
155 # Entity prototype: |
155 # Entity prototype: |
166 val = [isinstance(x, Model) and x._dbmodel or x for x in val] |
166 val = [isinstance(x, Model) and x._dbmodel or x for x in val] |
167 elif isinstance(val, Model): |
167 elif isinstance(val, Model): |
168 val = val._dbmodel |
168 val = val._dbmodel |
169 kwargs[key] = val.key() |
169 kwargs[key] = val.key() |
170 self._gaeinitargs = (args, kwargs) |
170 self._gaeinitargs = (args, kwargs) |
171 |
171 |
172 def __repr__(self): |
172 def __repr__(self): |
173 return '<ModelEntity %s %s %s at %s>' % ( |
173 return '<ModelEntity %s %s %s at %s>' % ( |
174 self.e_schema, self.eid, self.keys(), id(self)) |
174 self.e_schema, self.eid, self.keys(), id(self)) |
175 |
175 |
176 def _cubicweb_to_datastore(self, attr, value): |
176 def _cubicweb_to_datastore(self, attr, value): |
177 attr = attr[2:] # remove 's_' / 'o_' prefix |
177 attr = attr[2:] # remove 's_' / 'o_' prefix |
178 if attr in self._attributes: |
178 if attr in self._attributes: |
179 tschema = self.e_schema.destination(attr) |
179 tschema = self.e_schema.destination(attr) |
180 if tschema == 'String': |
180 if tschema == 'String': |
181 if len(value) > 500: |
181 if len(value) > 500: |
182 value = Text(value) |
182 value = Text(value) |
183 elif tschema == 'Password': |
183 elif tschema == 'Password': |
184 # if value is a Binary instance, this mean we got it |
184 # if value is a Binary instance, this mean we got it |
185 # from a query result and so it is already encrypted |
185 # from a query result and so it is already encrypted |
186 if isinstance(value, Binary): |
186 if isinstance(value, Binary): |
187 value = value.getvalue() |
187 value = value.getvalue() |
201 attr = 's_' + attr |
201 attr = 's_' + attr |
202 if value is not None and convert: |
202 if value is not None and convert: |
203 value = self._cubicweb_to_datastore(attr, value) |
203 value = self._cubicweb_to_datastore(attr, value) |
204 gaedict[attr] = value |
204 gaedict[attr] = value |
205 return gaedict |
205 return gaedict |
206 |
206 |
207 def to_gae_model(self): |
207 def to_gae_model(self): |
208 dbmodel = self._dbmodel |
208 dbmodel = self._dbmodel |
209 dbmodel.update(self._to_gae_dict()) |
209 dbmodel.update(self._to_gae_dict()) |
210 return dbmodel |
210 return dbmodel |
211 |
211 |
212 @property |
212 @property |
213 @cached |
213 @cached |
214 def _dbmodel(self): |
214 def _dbmodel(self): |
215 if self.has_eid(): |
215 if self.has_eid(): |
216 assert self._gaeinitargs is None |
216 assert self._gaeinitargs is None |
217 try: |
217 try: |
218 return self.req.datastore_get(self.eid) |
218 return self.req.datastore_get(self.eid) |
219 except AttributeError: # self.req is not a server session |
219 except AttributeError: # self.req is not a server session |
238 assert key_name is None |
238 assert key_name is None |
239 key_name = kwargs.pop('key_name') |
239 key_name = kwargs.pop('key_name') |
240 if '_app' in kwargs: |
240 if '_app' in kwargs: |
241 assert _app is None |
241 assert _app is None |
242 _app = kwargs.pop('_app') |
242 _app = kwargs.pop('_app') |
243 |
243 |
244 for key, value in kwargs.iteritems(): |
244 for key, value in kwargs.iteritems(): |
245 if key in self._attributes: |
245 if key in self._attributes: |
246 values['s_'+key] = value |
246 values['s_'+key] = value |
247 else: |
247 else: |
248 kwargs = None |
248 kwargs = None |
265 |
265 |
266 Note that if this function return something else than None, the returned |
266 Note that if this function return something else than None, the returned |
267 value will be prefixed by 'key_' to build the actual key name. |
267 value will be prefixed by 'key_' to build the actual key name. |
268 """ |
268 """ |
269 return None |
269 return None |
270 |
270 |
271 def metainformation(self): |
271 def metainformation(self): |
272 return {'type': self.id, 'source': {'uri': 'system'}, 'extid': None} |
272 return {'type': self.id, 'source': {'uri': 'system'}, 'extid': None} |
273 |
273 |
274 def view(self, vid, __registry='views', **kwargs): |
274 def view(self, vid, __registry='views', **kwargs): |
275 """shortcut to apply a view on this entity""" |
275 """shortcut to apply a view on this entity""" |
276 return self.vreg.render(__registry, vid, self.req, rset=self.rset, |
276 return self.vreg.render(__registry, vid, self.req, rset=self.rset, |
277 row=self.row, col=self.col, **kwargs) |
277 row=self.row, col=self.col, **kwargs) |
278 |
278 |
280 def _rest_attr_info(cls): |
280 def _rest_attr_info(cls): |
281 mainattr, needcheck = super(Model, cls)._rest_attr_info() |
281 mainattr, needcheck = super(Model, cls)._rest_attr_info() |
282 if needcheck: |
282 if needcheck: |
283 return 'eid', False |
283 return 'eid', False |
284 return mainattr, needcheck |
284 return mainattr, needcheck |
285 |
285 |
286 def get_value(self, name): |
286 def get_value(self, name): |
287 try: |
287 try: |
288 value = self[name] |
288 value = self[name] |
289 except KeyError: |
289 except KeyError: |
290 if not self.has_eid(): |
290 if not self.has_eid(): |
317 objs = Query(str(targettype)).Get(limit) |
317 objs = Query(str(targettype)).Get(limit) |
318 else: |
318 else: |
319 objs = Query(str(targettype)).Run() |
319 objs = Query(str(targettype)).Run() |
320 return rset_from_objs(self.req, objs, ('eid',), |
320 return rset_from_objs(self.req, objs, ('eid',), |
321 'Any X WHERE X is %s' % targettype) |
321 'Any X WHERE X is %s' % targettype) |
322 |
322 |
323 def key(self): |
323 def key(self): |
324 return Key(self.eid) |
324 return Key(self.eid) |
325 |
325 |
326 def put(self, req=None): |
326 def put(self, req=None): |
327 if req is not None and self.req is None: |
327 if req is not None and self.req is None: |
332 if self.req is not None and self.rset is None: |
332 if self.req is not None and self.rset is None: |
333 self.rset = rset_from_objs(self.req, dbmodel, ('eid',), |
333 self.rset = rset_from_objs(self.req, dbmodel, ('eid',), |
334 'Any X WHERE X eid %(x)s', {'x': self.eid}) |
334 'Any X WHERE X eid %(x)s', {'x': self.eid}) |
335 self.row = self.col = 0 |
335 self.row = self.col = 0 |
336 return dbmodel |
336 return dbmodel |
337 |
337 |
338 @needrequest |
338 @needrequest |
339 def get(cls, req, keys): |
339 def get(cls, req, keys): |
340 # if check if this is a dict.key call |
340 # if check if this is a dict.key call |
341 if isinstance(cls, Model) and keys in cls._attributes: |
341 if isinstance(cls, Model) and keys in cls._attributes: |
342 return super(Model, cls).get(keys) |
342 return super(Model, cls).get(keys) |
391 def properties(cls): |
391 def properties(cls): |
392 raise NotImplementedError('use eschema') |
392 raise NotImplementedError('use eschema') |
393 |
393 |
394 def dynamic_properties(self): |
394 def dynamic_properties(self): |
395 raise NotImplementedError('use eschema') |
395 raise NotImplementedError('use eschema') |
396 |
396 |
397 def is_saved(self): |
397 def is_saved(self): |
398 return self.has_eid() |
398 return self.has_eid() |
399 |
399 |
400 def parent(self): |
400 def parent(self): |
401 parent = self._dbmodel.parent() |
401 parent = self._dbmodel.parent() |
423 TextProperty = db.TextProperty |
423 TextProperty = db.TextProperty |
424 BlobProperty = db.BlobProperty |
424 BlobProperty = db.BlobProperty |
425 IntegerProperty = db.IntegerProperty |
425 IntegerProperty = db.IntegerProperty |
426 FloatProperty = db.FloatProperty |
426 FloatProperty = db.FloatProperty |
427 ListProperty = db.ListProperty |
427 ListProperty = db.ListProperty |
428 SelfReferenceProperty = db.SelfReferenceProperty |
428 SelfReferenceProperty = db.SelfReferenceProperty |
429 UserProperty = db.UserProperty |
429 UserProperty = db.UserProperty |
430 |
430 |
431 |
431 |
432 class ReferencePropertyStub(object): |
432 class ReferencePropertyStub(object): |
433 def __init__(self, cls, args, kwargs): |
433 def __init__(self, cls, args, kwargs): |