453 |
453 |
454 @classmethod |
454 @classmethod |
455 def _cw_build_entity_query(cls, kwargs): |
455 def _cw_build_entity_query(cls, kwargs): |
456 relations = [] |
456 relations = [] |
457 restrictions = set() |
457 restrictions = set() |
458 pending_relations = [] |
458 pendingrels = [] |
459 eschema = cls.e_schema |
459 eschema = cls.e_schema |
460 qargs = {} |
460 qargs = {} |
461 attrcache = {} |
461 attrcache = {} |
462 for attr, value in kwargs.items(): |
462 for attr, value in kwargs.items(): |
463 if attr.startswith('reverse_'): |
463 if attr.startswith('reverse_'): |
472 continue # avoid crash with empty IN clause |
472 continue # avoid crash with empty IN clause |
473 elif len(value) == 1: |
473 elif len(value) == 1: |
474 value = iter(value).next() |
474 value = iter(value).next() |
475 else: |
475 else: |
476 # prepare IN clause |
476 # prepare IN clause |
477 pending_relations.append( (attr, role, value) ) |
477 pendingrels.append( (attr, role, value) ) |
478 continue |
478 continue |
479 if rschema.final: # attribute |
479 if rschema.final: # attribute |
480 relations.append('X %s %%(%s)s' % (attr, attr)) |
480 relations.append('X %s %%(%s)s' % (attr, attr)) |
481 attrcache[attr] = value |
481 attrcache[attr] = value |
482 elif value is None: |
482 elif value is None: |
483 pending_relations.append( (attr, role, value) ) |
483 pendingrels.append( (attr, role, value) ) |
484 else: |
484 else: |
485 rvar = attr.upper() |
485 rvar = attr.upper() |
486 if role == 'object': |
486 if role == 'object': |
487 relations.append('%s %s X' % (rvar, attr)) |
487 relations.append('%s %s X' % (rvar, attr)) |
488 else: |
488 else: |
496 rql = u'' |
496 rql = u'' |
497 if relations: |
497 if relations: |
498 rql += ', '.join(relations) |
498 rql += ', '.join(relations) |
499 if restrictions: |
499 if restrictions: |
500 rql += ' WHERE %s' % ', '.join(restrictions) |
500 rql += ' WHERE %s' % ', '.join(restrictions) |
501 return rql, qargs, pending_relations, attrcache |
501 return rql, qargs, pendingrels, attrcache |
502 |
502 |
503 @classmethod |
503 @classmethod |
504 def _cw_handle_pending_relations(cls, eid, pending_relations, execute): |
504 def _cw_handle_pending_relations(cls, eid, pendingrels, execute): |
505 for attr, role, values in pending_relations: |
505 for attr, role, values in pendingrels: |
506 if role == 'object': |
506 if role == 'object': |
507 restr = 'Y %s X' % attr |
507 restr = 'Y %s X' % attr |
508 else: |
508 else: |
509 restr = 'X %s Y' % attr |
509 restr = 'X %s Y' % attr |
510 if values is None: |
510 if values is None: |
528 |
528 |
529 You can also set relations where the entity has 'object' role by |
529 You can also set relations where the entity has 'object' role by |
530 prefixing the relation name by 'reverse_'. Also, relation values may be |
530 prefixing the relation name by 'reverse_'. Also, relation values may be |
531 an entity or eid, a list of entities or eids. |
531 an entity or eid, a list of entities or eids. |
532 """ |
532 """ |
533 rql, qargs, pending_relations, attrcache = cls._cw_build_entity_query(kwargs) |
533 rql, qargs, pendingrels, attrcache = cls._cw_build_entity_query(kwargs) |
534 if rql: |
534 if rql: |
535 rql = 'INSERT %s X: %s' % (cls.__regid__, rql) |
535 rql = 'INSERT %s X: %s' % (cls.__regid__, rql) |
536 else: |
536 else: |
537 rql = 'INSERT %s X' % (cls.__regid__) |
537 rql = 'INSERT %s X' % (cls.__regid__) |
538 created = execute(rql, qargs).get_entity(0, 0) |
538 created = execute(rql, qargs).get_entity(0, 0) |
|
539 created._cw_update_attr_cache(attrcache) |
539 created.cw_attr_cache.update(attrcache) |
540 created.cw_attr_cache.update(attrcache) |
540 cls._cw_handle_pending_relations(created.eid, pending_relations, execute) |
541 cls._cw_handle_pending_relations(created.eid, pendingrels, execute) |
541 return created |
542 return created |
542 |
543 |
543 def __init__(self, req, rset=None, row=None, col=0): |
544 def __init__(self, req, rset=None, row=None, col=0): |
544 AppObject.__init__(self, req, rset=rset, row=row, col=col) |
545 AppObject.__init__(self, req, rset=rset, row=row, col=col) |
545 self._cw_related_cache = {} |
546 self._cw_related_cache = {} |
554 return '<Entity %s %s %s at %s>' % ( |
555 return '<Entity %s %s %s at %s>' % ( |
555 self.e_schema, self.eid, self.cw_attr_cache.keys(), id(self)) |
556 self.e_schema, self.eid, self.cw_attr_cache.keys(), id(self)) |
556 |
557 |
557 def __cmp__(self, other): |
558 def __cmp__(self, other): |
558 raise NotImplementedError('comparison not implemented for %s' % self.__class__) |
559 raise NotImplementedError('comparison not implemented for %s' % self.__class__) |
|
560 |
|
561 def _cw_update_attr_cache(self, attrcache): |
|
562 for key in self._cw.get_shared_data('%s.dont-cache-attrs' % self.eid, |
|
563 default=(), txdata=True, pop=True): |
|
564 attrcache.pop(key, None) |
|
565 self.cw_attr_cache.update(attrcache) |
|
566 |
|
567 def _cw_dont_cache_attribute(self, attr): |
|
568 """repository side method called when some attribute have been |
|
569 transformed by a hook, hence original value should not be cached by |
|
570 client |
|
571 """ |
|
572 self._cw.transaction_data.setdefault('%s.dont-cache-attrs' % self.eid, set()).add(attr) |
559 |
573 |
560 def __json_encode__(self): |
574 def __json_encode__(self): |
561 """custom json dumps hook to dump the entity's eid |
575 """custom json dumps hook to dump the entity's eid |
562 which is not part of dict structure itself |
576 which is not part of dict structure itself |
563 """ |
577 """ |
1252 """ |
1266 """ |
1253 _check_cw_unsafe(kwargs) |
1267 _check_cw_unsafe(kwargs) |
1254 assert kwargs |
1268 assert kwargs |
1255 assert self.cw_is_saved(), "should not call set_attributes while entity "\ |
1269 assert self.cw_is_saved(), "should not call set_attributes while entity "\ |
1256 "hasn't been saved yet" |
1270 "hasn't been saved yet" |
1257 rql, qargs, pending_relations, attrcache = self._cw_build_entity_query(kwargs) |
1271 rql, qargs, pendingrels, attrcache = self._cw_build_entity_query(kwargs) |
1258 if rql: |
1272 if rql: |
1259 rql = 'SET ' + rql |
1273 rql = 'SET ' + rql |
1260 qargs['x'] = self.eid |
1274 qargs['x'] = self.eid |
1261 if ' WHERE ' in rql: |
1275 if ' WHERE ' in rql: |
1262 rql += ', X eid %(x)s' |
1276 rql += ', X eid %(x)s' |
1264 rql += ' WHERE X eid %(x)s' |
1278 rql += ' WHERE X eid %(x)s' |
1265 self._cw.execute(rql, qargs) |
1279 self._cw.execute(rql, qargs) |
1266 # update current local object _after_ the rql query to avoid |
1280 # update current local object _after_ the rql query to avoid |
1267 # interferences between the query execution itself and the cw_edited / |
1281 # interferences between the query execution itself and the cw_edited / |
1268 # skip_security machinery |
1282 # skip_security machinery |
1269 self.cw_attr_cache.update(attrcache) |
1283 self._cw_update_attr_cache(attrcache) |
1270 self._cw_handle_pending_relations(self.eid, pending_relations, self._cw.execute) |
1284 self._cw_handle_pending_relations(self.eid, pendingrels, self._cw.execute) |
1271 # XXX update relation cache |
1285 # XXX update relation cache |
1272 |
1286 |
1273 def cw_delete(self, **kwargs): |
1287 def cw_delete(self, **kwargs): |
1274 assert self.has_eid(), self.eid |
1288 assert self.has_eid(), self.eid |
1275 self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |
1289 self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |