450 if mainattr == 'eid': |
450 if mainattr == 'eid': |
451 needcheck = False |
451 needcheck = False |
452 return mainattr, needcheck |
452 return mainattr, needcheck |
453 |
453 |
454 @classmethod |
454 @classmethod |
455 def cw_instantiate(cls, execute, **kwargs): |
455 def _cw_build_entity_query(cls, kwargs): |
456 """add a new entity of this given type |
|
457 |
|
458 Example (in a shell session): |
|
459 |
|
460 >>> companycls = vreg['etypes'].etype_class(('Company') |
|
461 >>> personcls = vreg['etypes'].etype_class(('Person') |
|
462 >>> c = companycls.cw_instantiate(session.execute, name=u'Logilab') |
|
463 >>> p = personcls.cw_instantiate(session.execute, firstname=u'John', lastname=u'Doe', |
|
464 ... works_for=c) |
|
465 |
|
466 You can also set relation where the entity has 'object' role by |
|
467 prefixing the relation by 'reverse_'. |
|
468 """ |
|
469 rql = 'INSERT %s X' % cls.__regid__ |
|
470 relations = [] |
456 relations = [] |
471 restrictions = set() |
457 restrictions = set() |
472 pending_relations = [] |
458 pending_relations = [] |
473 eschema = cls.e_schema |
459 eschema = cls.e_schema |
474 qargs = {} |
460 qargs = {} |
|
461 attrcache = {} |
475 for attr, value in kwargs.items(): |
462 for attr, value in kwargs.items(): |
476 if attr.startswith('reverse_'): |
463 if attr.startswith('reverse_'): |
477 attr = attr[len('reverse_'):] |
464 attr = attr[len('reverse_'):] |
478 role = 'object' |
465 role = 'object' |
479 else: |
466 else: |
501 if not restriction in restrictions: |
491 if not restriction in restrictions: |
502 restrictions.add(restriction) |
492 restrictions.add(restriction) |
503 if hasattr(value, 'eid'): |
493 if hasattr(value, 'eid'): |
504 value = value.eid |
494 value = value.eid |
505 qargs[attr] = value |
495 qargs[attr] = value |
|
496 rql = u'' |
506 if relations: |
497 if relations: |
507 rql = '%s: %s' % (rql, ', '.join(relations)) |
498 rql += ', '.join(relations) |
508 if restrictions: |
499 if restrictions: |
509 rql = '%s WHERE %s' % (rql, ', '.join(restrictions)) |
500 rql += ' WHERE %s' % ', '.join(restrictions) |
510 created = execute(rql, qargs).get_entity(0, 0) |
501 return rql, qargs, pending_relations, attrcache |
|
502 |
|
503 @classmethod |
|
504 def _cw_handle_pending_relations(cls, eid, pending_relations, execute): |
511 for attr, role, values in pending_relations: |
505 for attr, role, values in pending_relations: |
512 if role == 'object': |
506 if role == 'object': |
513 restr = 'Y %s X' % attr |
507 restr = 'Y %s X' % attr |
514 else: |
508 else: |
515 restr = 'X %s Y' % attr |
509 restr = 'X %s Y' % attr |
|
510 if values is None: |
|
511 execute('DELETE %s WHERE X eid %%(x)s' % restr, {'x': eid}) |
|
512 continue |
516 execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( |
513 execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( |
517 restr, ','.join(str(getattr(r, 'eid', r)) for r in values)), |
514 restr, ','.join(str(getattr(r, 'eid', r)) for r in values)), |
518 {'x': created.eid}, build_descr=False) |
515 {'x': eid}, build_descr=False) |
|
516 |
|
517 @classmethod |
|
518 def cw_instantiate(cls, execute, **kwargs): |
|
519 """add a new entity of this given type |
|
520 |
|
521 Example (in a shell session): |
|
522 |
|
523 >>> companycls = vreg['etypes'].etype_class(('Company') |
|
524 >>> personcls = vreg['etypes'].etype_class(('Person') |
|
525 >>> c = companycls.cw_instantiate(session.execute, name=u'Logilab') |
|
526 >>> p = personcls.cw_instantiate(session.execute, firstname=u'John', lastname=u'Doe', |
|
527 ... works_for=c) |
|
528 |
|
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 |
|
531 an entity or eid, a list of entities or eids. |
|
532 """ |
|
533 rql, qargs, pending_relations, attrcache = cls._cw_build_entity_query(kwargs) |
|
534 if rql: |
|
535 rql = 'INSERT %s X: %s' % (cls.__regid__, rql) |
|
536 else: |
|
537 rql = 'INSERT %s X' % (cls.__regid__) |
|
538 created = execute(rql, qargs).get_entity(0, 0) |
|
539 created.cw_attr_cache.update(attrcache) |
|
540 cls._cw_handle_pending_relations(created.eid, pending_relations, execute) |
519 return created |
541 return created |
520 |
542 |
521 def __init__(self, req, rset=None, row=None, col=0): |
543 def __init__(self, req, rset=None, row=None, col=0): |
522 AppObject.__init__(self, req, rset=rset, row=row, col=col) |
544 AppObject.__init__(self, req, rset=rset, row=row, col=col) |
523 self._cw_related_cache = {} |
545 self._cw_related_cache = {} |
1210 except AttributeError: |
1232 except AttributeError: |
1211 pass |
1233 pass |
1212 |
1234 |
1213 # raw edition utilities ################################################### |
1235 # raw edition utilities ################################################### |
1214 |
1236 |
1215 def set_attributes(self, **kwargs): # XXX cw_set_attributes |
1237 def cw_set(self, **kwargs): |
|
1238 """update this entity using given attributes / relation, working in the |
|
1239 same fashion as :meth:`cw_instantiate`. |
|
1240 |
|
1241 Example (in a shell session): |
|
1242 |
|
1243 >>> c = rql('Any X WHERE X is Company').get_entity(0, 0) |
|
1244 >>> p = rql('Any X WHERE X is Person').get_entity(0, 0) |
|
1245 >>> c.set(name=u'Logilab') |
|
1246 >>> p.set(firstname=u'John', lastname=u'Doe', works_for=c) |
|
1247 |
|
1248 You can also set relations where the entity has 'object' role by |
|
1249 prefixing the relation name by 'reverse_'. Also, relation values may be |
|
1250 an entity or eid, a list of entities or eids, or None (meaning that all |
|
1251 relations of the given type from or to this object should be deleted). |
|
1252 """ |
1216 _check_cw_unsafe(kwargs) |
1253 _check_cw_unsafe(kwargs) |
1217 assert kwargs |
1254 assert kwargs |
1218 assert self.cw_is_saved(), "should not call set_attributes while entity "\ |
1255 assert self.cw_is_saved(), "should not call set_attributes while entity "\ |
1219 "hasn't been saved yet" |
1256 "hasn't been saved yet" |
1220 relations = ['X %s %%(%s)s' % (key, key) for key in kwargs] |
1257 rql, qargs, pending_relations, attrcache = self._cw_build_entity_query(kwargs) |
1221 # and now update the database |
1258 if rql: |
1222 kwargs['x'] = self.eid |
1259 rql = 'SET ' + rql |
1223 self._cw.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations), |
1260 qargs['x'] = self.eid |
1224 kwargs) |
1261 if ' WHERE ' in rql: |
1225 kwargs.pop('x') |
1262 rql += ', X eid %(x)s' |
|
1263 else: |
|
1264 rql += ' WHERE X eid %(x)s' |
|
1265 self._cw.execute(rql, qargs) |
1226 # update current local object _after_ the rql query to avoid |
1266 # update current local object _after_ the rql query to avoid |
1227 # interferences between the query execution itself and the cw_edited / |
1267 # interferences between the query execution itself and the cw_edited / |
1228 # skip_security machinery |
1268 # skip_security machinery |
1229 self.cw_attr_cache.update(kwargs) |
1269 self.cw_attr_cache.update(attrcache) |
1230 |
1270 self._cw_handle_pending_relations(self.eid, pending_relations, self._cw.execute) |
1231 def set_relations(self, **kwargs): # XXX cw_set_relations |
1271 # XXX update relation cache |
1232 """add relations to the given object. To set a relation where this entity |
|
1233 is the object of the relation, use 'reverse_'<relation> as argument name. |
|
1234 |
|
1235 Values may be an entity or eid, a list of entities or eids, or None |
|
1236 (meaning that all relations of the given type from or to this object |
|
1237 should be deleted). |
|
1238 """ |
|
1239 # XXX update cache |
|
1240 _check_cw_unsafe(kwargs) |
|
1241 for attr, values in kwargs.iteritems(): |
|
1242 if attr.startswith('reverse_'): |
|
1243 restr = 'Y %s X' % attr[len('reverse_'):] |
|
1244 else: |
|
1245 restr = 'X %s Y' % attr |
|
1246 if values is None: |
|
1247 self._cw.execute('DELETE %s WHERE X eid %%(x)s' % restr, |
|
1248 {'x': self.eid}) |
|
1249 continue |
|
1250 if not isinstance(values, (tuple, list, set, frozenset)): |
|
1251 values = (values,) |
|
1252 eids = [] |
|
1253 for val in values: |
|
1254 try: |
|
1255 eids.append(str(val.eid)) |
|
1256 except AttributeError: |
|
1257 try: |
|
1258 eids.append(str(typed_eid(val))) |
|
1259 except (ValueError, TypeError): |
|
1260 raise Exception('expected an Entity or eid, got %s' % val) |
|
1261 self._cw.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( |
|
1262 restr, ','.join(eids)), {'x': self.eid}) |
|
1263 |
1272 |
1264 def cw_delete(self, **kwargs): |
1273 def cw_delete(self, **kwargs): |
1265 assert self.has_eid(), self.eid |
1274 assert self.has_eid(), self.eid |
1266 self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |
1275 self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |
1267 {'x': self.eid}, **kwargs) |
1276 {'x': self.eid}, **kwargs) |
1271 def _cw_clear_local_perm_cache(self, action): |
1280 def _cw_clear_local_perm_cache(self, action): |
1272 for rqlexpr in self.e_schema.get_rqlexprs(action): |
1281 for rqlexpr in self.e_schema.get_rqlexprs(action): |
1273 self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) |
1282 self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) |
1274 |
1283 |
1275 # deprecated stuff ######################################################### |
1284 # deprecated stuff ######################################################### |
|
1285 |
|
1286 @deprecated('[3.15] use cw_set() instead') |
|
1287 def set_attributes(self, **kwargs): # XXX cw_set_attributes |
|
1288 self.cw_set(**kwargs) |
|
1289 |
|
1290 @deprecated('[3.15] use cw_set() instead') |
|
1291 def set_relations(self, **kwargs): # XXX cw_set_relations |
|
1292 """add relations to the given object. To set a relation where this entity |
|
1293 is the object of the relation, use 'reverse_'<relation> as argument name. |
|
1294 |
|
1295 Values may be an entity or eid, a list of entities or eids, or None |
|
1296 (meaning that all relations of the given type from or to this object |
|
1297 should be deleted). |
|
1298 """ |
|
1299 self.cw_set(**kwargs) |
1276 |
1300 |
1277 @deprecated('[3.13] use entity.cw_clear_all_caches()') |
1301 @deprecated('[3.13] use entity.cw_clear_all_caches()') |
1278 def clear_all_caches(self): |
1302 def clear_all_caches(self): |
1279 return self.cw_clear_all_caches() |
1303 return self.cw_clear_all_caches() |
1280 |
1304 |