13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
14 # details. |
14 # details. |
15 # |
15 # |
16 # You should have received a copy of the GNU Lesser General Public License along |
16 # You should have received a copy of the GNU Lesser General Public License along |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
18 """Base class for entity objects manipulated in clients |
18 """Base class for entity objects manipulated in clients""" |
19 |
19 |
20 """ |
|
21 __docformat__ = "restructuredtext en" |
20 __docformat__ = "restructuredtext en" |
22 |
21 |
23 from warnings import warn |
22 from warnings import warn |
24 |
23 |
25 from logilab.common import interface |
24 from logilab.common import interface |
26 from logilab.common.compat import all |
|
27 from logilab.common.decorators import cached |
25 from logilab.common.decorators import cached |
|
26 from logilab.common.deprecation import deprecated |
28 from logilab.mtconverter import TransformData, TransformError, xml_escape |
27 from logilab.mtconverter import TransformData, TransformError, xml_escape |
29 |
28 |
30 from rql.utils import rqlvar_maker |
29 from rql.utils import rqlvar_maker |
31 |
30 |
32 from cubicweb import Unauthorized, typed_eid |
31 from cubicweb import Unauthorized, typed_eid |
411 adapter = self._cw.vreg['adapters'].select_or_none( |
410 adapter = self._cw.vreg['adapters'].select_or_none( |
412 interface, self._cw, entity=self) |
411 interface, self._cw, entity=self) |
413 cache[interface] = adapter |
412 cache[interface] = adapter |
414 return adapter |
413 return adapter |
415 |
414 |
416 def rql_set_value(self, attr, value): |
415 def has_eid(self): # XXX cw_has_eid |
417 """call by rql execution plan when some attribute is modified |
|
418 |
|
419 don't use dict api in such case since we don't want attribute to be |
|
420 added to skip_security_attributes. |
|
421 """ |
|
422 super(Entity, self).__setitem__(attr, value) |
|
423 |
|
424 def pre_add_hook(self): |
|
425 """hook called by the repository before doing anything to add the entity |
|
426 (before_add entity hooks have not been called yet). This give the |
|
427 occasion to do weird stuff such as autocast (File -> Image for instance). |
|
428 |
|
429 This method must return the actual entity to be added. |
|
430 """ |
|
431 return self |
|
432 |
|
433 def set_eid(self, eid): |
|
434 self.eid = eid |
|
435 |
|
436 def has_eid(self): |
|
437 """return True if the entity has an attributed eid (False |
416 """return True if the entity has an attributed eid (False |
438 meaning that the entity has to be created |
417 meaning that the entity has to be created |
439 """ |
418 """ |
440 try: |
419 try: |
441 typed_eid(self.eid) |
420 typed_eid(self.eid) |
442 return True |
421 return True |
443 except (ValueError, TypeError): |
422 except (ValueError, TypeError): |
444 return False |
423 return False |
445 |
424 |
446 def is_saved(self): |
425 def cw_is_saved(self): |
447 """during entity creation, there is some time during which the entity |
426 """during entity creation, there is some time during which the entity |
448 has an eid attributed though it's not saved (eg during before_add_entity |
427 has an eid attributed though it's not saved (eg during |
449 hooks). You can use this method to ensure the entity has an eid *and* is |
428 'before_add_entity' hooks). You can use this method to ensure the entity |
450 saved in its source. |
429 has an eid *and* is saved in its source. |
451 """ |
430 """ |
452 return self.has_eid() and self._is_saved |
431 return self.has_eid() and self._cw_is_saved |
453 |
432 |
454 @cached |
433 @cached |
455 def metainformation(self): |
434 def cw_metainformation(self): |
456 res = dict(zip(('type', 'source', 'extid'), self._cw.describe(self.eid))) |
435 res = dict(zip(('type', 'source', 'extid'), self._cw.describe(self.eid))) |
457 res['source'] = self._cw.source_defs()[res['source']] |
436 res['source'] = self._cw.source_defs()[res['source']] |
458 return res |
437 return res |
459 |
438 |
460 def clear_local_perm_cache(self, action): |
439 def cw_check_perm(self, action): |
461 for rqlexpr in self.e_schema.get_rqlexprs(action): |
|
462 self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) |
|
463 |
|
464 def check_perm(self, action): |
|
465 self.e_schema.check_perm(self._cw, action, eid=self.eid) |
440 self.e_schema.check_perm(self._cw, action, eid=self.eid) |
466 |
441 |
467 def has_perm(self, action): |
442 def cw_has_perm(self, action): |
468 return self.e_schema.has_perm(self._cw, action, eid=self.eid) |
443 return self.e_schema.has_perm(self._cw, action, eid=self.eid) |
469 |
444 |
470 def view(self, __vid, __registry='views', w=None, **kwargs): |
445 def view(self, __vid, __registry='views', w=None, **kwargs): # XXX cw_view |
471 """shortcut to apply a view on this entity""" |
446 """shortcut to apply a view on this entity""" |
472 view = self._cw.vreg[__registry].select(__vid, self._cw, rset=self.cw_rset, |
447 view = self._cw.vreg[__registry].select(__vid, self._cw, rset=self.cw_rset, |
473 row=self.cw_row, col=self.cw_col, |
448 row=self.cw_row, col=self.cw_col, |
474 **kwargs) |
449 **kwargs) |
475 return view.render(row=self.cw_row, col=self.cw_col, w=w, **kwargs) |
450 return view.render(row=self.cw_row, col=self.cw_col, w=w, **kwargs) |
476 |
451 |
477 def absolute_url(self, *args, **kwargs): |
452 def absolute_url(self, *args, **kwargs): # XXX cw_url |
478 """return an absolute url to view this entity""" |
453 """return an absolute url to view this entity""" |
479 # use *args since we don't want first argument to be "anonymous" to |
454 # use *args since we don't want first argument to be "anonymous" to |
480 # avoid potential clash with kwargs |
455 # avoid potential clash with kwargs |
481 if args: |
456 if args: |
482 assert len(args) == 1, 'only 0 or 1 non-named-argument expected' |
457 assert len(args) == 1, 'only 0 or 1 non-named-argument expected' |
520 if nbresults != 1: # ambiguity? |
495 if nbresults != 1: # ambiguity? |
521 mainattr = 'eid' |
496 mainattr = 'eid' |
522 path += '/eid' |
497 path += '/eid' |
523 if mainattr == 'eid': |
498 if mainattr == 'eid': |
524 if use_ext_eid: |
499 if use_ext_eid: |
525 value = self.metainformation()['extid'] |
500 value = self.cw_metainformation()['extid'] |
526 else: |
501 else: |
527 value = self.eid |
502 value = self.eid |
528 return '%s/%s' % (path, self._cw.url_quote(value)) |
503 return '%s/%s' % (path, self._cw.url_quote(value)) |
529 |
504 |
530 def attr_metadata(self, attr, metadata): |
505 def cw_attr_metadata(self, attr, metadata): |
531 """return a metadata for an attribute (None if unspecified)""" |
506 """return a metadata for an attribute (None if unspecified)""" |
532 value = getattr(self, '%s_%s' % (attr, metadata), None) |
507 value = getattr(self, '%s_%s' % (attr, metadata), None) |
533 if value is None and metadata == 'encoding': |
508 if value is None and metadata == 'encoding': |
534 value = self._cw.vreg.property_value('ui.encoding') |
509 value = self._cw.vreg.property_value('ui.encoding') |
535 return value |
510 return value |
536 |
511 |
537 def printable_value(self, attr, value=_marker, attrtype=None, |
512 def printable_value(self, attr, value=_marker, attrtype=None, |
538 format='text/html', displaytime=True): |
513 format='text/html', displaytime=True): # XXX cw_printable_value |
539 """return a displayable value (i.e. unicode string) which may contains |
514 """return a displayable value (i.e. unicode string) which may contains |
540 html tags |
515 html tags |
541 """ |
516 """ |
542 attr = str(attr) |
517 attr = str(attr) |
543 if value is _marker: |
518 if value is _marker: |
552 if attrtype == 'String': |
527 if attrtype == 'String': |
553 # internalinalized *and* formatted string such as schema |
528 # internalinalized *and* formatted string such as schema |
554 # description... |
529 # description... |
555 if props.internationalizable: |
530 if props.internationalizable: |
556 value = self._cw._(value) |
531 value = self._cw._(value) |
557 attrformat = self.attr_metadata(attr, 'format') |
532 attrformat = self.cw_attr_metadata(attr, 'format') |
558 if attrformat: |
533 if attrformat: |
559 return self.mtc_transform(value, attrformat, format, |
534 return self._cw_mtc_transform(value, attrformat, format, |
560 self._cw.encoding) |
535 self._cw.encoding) |
561 elif attrtype == 'Bytes': |
536 elif attrtype == 'Bytes': |
562 attrformat = self.attr_metadata(attr, 'format') |
537 attrformat = self.cw_attr_metadata(attr, 'format') |
563 if attrformat: |
538 if attrformat: |
564 encoding = self.attr_metadata(attr, 'encoding') |
539 encoding = self.cw_attr_metadata(attr, 'encoding') |
565 return self.mtc_transform(value.getvalue(), attrformat, format, |
540 return self._cw_mtc_transform(value.getvalue(), attrformat, format, |
566 encoding) |
541 encoding) |
567 return u'' |
542 return u'' |
568 value = printable_value(self._cw, attrtype, value, props, |
543 value = printable_value(self._cw, attrtype, value, props, |
569 displaytime=displaytime) |
544 displaytime=displaytime) |
570 if format == 'text/html': |
545 if format == 'text/html': |
571 value = xml_escape(value) |
546 value = xml_escape(value) |
572 return value |
547 return value |
573 |
548 |
574 def mtc_transform(self, data, format, target_format, encoding, |
549 def _cw_mtc_transform(self, data, format, target_format, encoding, |
575 _engine=ENGINE): |
550 _engine=ENGINE): |
576 trdata = TransformData(data, format, encoding, appobject=self) |
551 trdata = TransformData(data, format, encoding, appobject=self) |
577 data = _engine.convert(trdata, target_format).decode() |
552 data = _engine.convert(trdata, target_format).decode() |
578 if format == 'text/html': |
553 if format == 'text/html': |
579 data = soup2xhtml(data, self._cw.encoding) |
554 data = soup2xhtml(data, self._cw.encoding) |
580 return data |
555 return data |
581 |
556 |
582 # entity cloning ########################################################## |
557 # entity cloning ########################################################## |
583 |
558 |
584 def copy_relations(self, ceid): |
559 def copy_relations(self, ceid): # XXX cw_copy_relations |
585 """copy relations of the object with the given eid on this |
560 """copy relations of the object with the given eid on this |
586 object (this method is called on the newly created copy, and |
561 object (this method is called on the newly created copy, and |
587 ceid designates the original entity). |
562 ceid designates the original entity). |
588 |
563 |
589 By default meta and composite relations are skipped. |
564 By default meta and composite relations are skipped. |
626 if rdef.cardinality[0] in '?1': |
601 if rdef.cardinality[0] in '?1': |
627 continue |
602 continue |
628 rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % ( |
603 rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % ( |
629 rschema.type, rschema.type) |
604 rschema.type, rschema.type) |
630 execute(rql, {'x': self.eid, 'y': ceid}) |
605 execute(rql, {'x': self.eid, 'y': ceid}) |
631 self.clear_related_cache(rschema.type, 'object') |
606 self.cw_clear_relation_cache(rschema.type, 'object') |
632 |
607 |
633 # data fetching methods ################################################### |
608 # data fetching methods ################################################### |
634 |
609 |
635 @cached |
610 @cached |
636 def as_rset(self): |
611 def as_rset(self): # XXX .cw_as_rset |
637 """returns a resultset containing `self` information""" |
612 """returns a resultset containing `self` information""" |
638 rset = ResultSet([(self.eid,)], 'Any X WHERE X eid %(x)s', |
613 rset = ResultSet([(self.eid,)], 'Any X WHERE X eid %(x)s', |
639 {'x': self.eid}, [(self.__regid__,)]) |
614 {'x': self.eid}, [(self.__regid__,)]) |
640 rset.req = self._cw |
615 rset.req = self._cw |
641 return rset |
616 return rset |
642 |
617 |
643 def to_complete_relations(self): |
618 def _cw_to_complete_relations(self): |
644 """by default complete final relations to when calling .complete()""" |
619 """by default complete final relations to when calling .complete()""" |
645 for rschema in self.e_schema.subject_relations(): |
620 for rschema in self.e_schema.subject_relations(): |
646 if rschema.final: |
621 if rschema.final: |
647 continue |
622 continue |
648 targets = rschema.objects(self.e_schema) |
623 targets = rschema.objects(self.e_schema) |
701 selected.append((attr, var)) |
676 selected.append((attr, var)) |
702 # +1 since this doen't include the main variable |
677 # +1 since this doen't include the main variable |
703 lastattr = len(selected) + 1 |
678 lastattr = len(selected) + 1 |
704 if attributes is None: |
679 if attributes is None: |
705 # fetch additional relations (restricted to 0..1 relations) |
680 # fetch additional relations (restricted to 0..1 relations) |
706 for rschema, role in self.to_complete_relations(): |
681 for rschema, role in self._cw_to_complete_relations(): |
707 rtype = rschema.type |
682 rtype = rschema.type |
708 if self.relation_cached(rtype, role): |
683 if self.cw_relation_cached(rtype, role): |
709 continue |
684 continue |
710 var = varmaker.next() |
685 var = varmaker.next() |
711 targettype = rschema.targets(self.e_schema, role)[0] |
686 targettype = rschema.targets(self.e_schema, role)[0] |
712 rdef = rschema.role_rdef(self.e_schema, targettype, role) |
687 rdef = rschema.role_rdef(self.e_schema, targettype, role) |
713 card = rdef.role_cardinality(role) |
688 card = rdef.role_cardinality(role) |
740 if value is None: |
715 if value is None: |
741 rrset = ResultSet([], rql, {'x': self.eid}) |
716 rrset = ResultSet([], rql, {'x': self.eid}) |
742 rrset.req = self._cw |
717 rrset.req = self._cw |
743 else: |
718 else: |
744 rrset = self._cw.eid_rset(value) |
719 rrset = self._cw.eid_rset(value) |
745 self.set_related_cache(rtype, role, rrset) |
720 self.cw_set_relation_cache(rtype, role, rrset) |
746 |
721 |
747 def get_value(self, name): |
722 def cw_attr_value(self, name): |
748 """get value for the attribute relation <name>, query the repository |
723 """get value for the attribute relation <name>, query the repository |
749 to get the value if necessary. |
724 to get the value if necessary. |
750 |
725 |
751 :type name: str |
726 :type name: str |
752 :param name: name of the attribute to get |
727 :param name: name of the attribute to get |
753 """ |
728 """ |
754 try: |
729 try: |
755 value = self[name] |
730 value = self[name] |
756 except KeyError: |
731 except KeyError: |
757 if not self.is_saved(): |
732 if not self.cw_is_saved(): |
758 return None |
733 return None |
759 rql = "Any A WHERE X eid %%(x)s, X %s A" % name |
734 rql = "Any A WHERE X eid %%(x)s, X %s A" % name |
760 try: |
735 try: |
761 rset = self._cw.execute(rql, {'x': self.eid}) |
736 rset = self._cw.execute(rql, {'x': self.eid}) |
762 except Unauthorized: |
737 except Unauthorized: |
774 self[name] = value = self._cw._('unaccessible') |
749 self[name] = value = self._cw._('unaccessible') |
775 else: |
750 else: |
776 self[name] = value = None |
751 self[name] = value = None |
777 return value |
752 return value |
778 |
753 |
779 def related(self, rtype, role='subject', limit=None, entities=False): |
754 def related(self, rtype, role='subject', limit=None, entities=False): # XXX .cw_related |
780 """returns a resultset of related entities |
755 """returns a resultset of related entities |
781 |
756 |
782 :param role: is the role played by 'self' in the relation ('subject' or 'object') |
757 :param role: is the role played by 'self' in the relation ('subject' or 'object') |
783 :param limit: resultset's maximum size |
758 :param limit: resultset's maximum size |
784 :param entities: if True, the entites are returned; if False, a result set is returned |
759 :param entities: if True, the entites are returned; if False, a result set is returned |
785 """ |
760 """ |
786 try: |
761 try: |
787 return self.related_cache(rtype, role, entities, limit) |
762 return self._cw_relation_cache(rtype, role, entities, limit) |
788 except KeyError: |
763 except KeyError: |
789 pass |
764 pass |
790 assert self.has_eid() |
765 assert self.has_eid() |
791 rql = self.related_rql(rtype, role) |
766 rql = self.cw_related_rql(rtype, role) |
792 rset = self._cw.execute(rql, {'x': self.eid}) |
767 rset = self._cw.execute(rql, {'x': self.eid}) |
793 self.set_related_cache(rtype, role, rset) |
768 self.cw_set_relation_cache(rtype, role, rset) |
794 return self.related(rtype, role, limit, entities) |
769 return self.related(rtype, role, limit, entities) |
795 |
770 |
796 def related_rql(self, rtype, role='subject', targettypes=None): |
771 def cw_related_rql(self, rtype, role='subject', targettypes=None): |
797 rschema = self._cw.vreg.schema[rtype] |
772 rschema = self._cw.vreg.schema[rtype] |
798 if role == 'subject': |
773 if role == 'subject': |
799 restriction = 'E eid %%(x)s, E %s X' % rtype |
774 restriction = 'E eid %%(x)s, E %s X' % rtype |
800 if targettypes is None: |
775 if targettypes is None: |
801 targettypes = rschema.objects(self.e_schema) |
776 targettypes = rschema.objects(self.e_schema) |
902 select.solutions, args, existant) |
877 select.solutions, args, existant) |
903 rql = rqlst.as_string() |
878 rql = rqlst.as_string() |
904 return rql, args |
879 return rql, args |
905 |
880 |
906 def unrelated(self, rtype, targettype, role='subject', limit=None, |
881 def unrelated(self, rtype, targettype, role='subject', limit=None, |
907 ordermethod=None): |
882 ordermethod=None): # XXX .cw_unrelated |
908 """return a result set of target type objects that may be related |
883 """return a result set of target type objects that may be related |
909 by a given relation, with self as subject or object |
884 by a given relation, with self as subject or object |
910 """ |
885 """ |
911 try: |
886 try: |
912 rql, args = self.unrelated_rql(rtype, targettype, role, ordermethod) |
887 rql, args = self.cw_unrelated_rql(rtype, targettype, role, ordermethod) |
913 except Unauthorized: |
888 except Unauthorized: |
914 return self._cw.empty_rset() |
889 return self._cw.empty_rset() |
915 if limit is not None: |
890 if limit is not None: |
916 before, after = rql.split(' WHERE ', 1) |
891 before, after = rql.split(' WHERE ', 1) |
917 rql = '%s LIMIT %s WHERE %s' % (before, limit, after) |
892 rql = '%s LIMIT %s WHERE %s' % (before, limit, after) |
918 return self._cw.execute(rql, args) |
893 return self._cw.execute(rql, args) |
919 |
894 |
920 # relations cache handling ################################################ |
895 # relations cache handling ################################################# |
921 |
896 |
922 def relation_cached(self, rtype, role): |
897 def cw_relation_cached(self, rtype, role): |
923 """return true if the given relation is already cached on the instance |
898 """return None if the given relation isn't already cached on the |
924 """ |
899 instance, else the content of the cache (a 2-uple (rset, entities)). |
925 return self._related_cache.get('%s_%s' % (rtype, role)) |
900 """ |
926 |
901 return self._cw_related_cache.get('%s_%s' % (rtype, role)) |
927 def related_cache(self, rtype, role, entities=True, limit=None): |
902 |
|
903 def _cw_relation_cache(self, rtype, role, entities=True, limit=None): |
928 """return values for the given relation if it's cached on the instance, |
904 """return values for the given relation if it's cached on the instance, |
929 else raise `KeyError` |
905 else raise `KeyError` |
930 """ |
906 """ |
931 res = self._related_cache['%s_%s' % (rtype, role)][entities] |
907 res = self._cw_related_cache['%s_%s' % (rtype, role)][entities] |
932 if limit is not None and limit < len(res): |
908 if limit is not None and limit < len(res): |
933 if entities: |
909 if entities: |
934 res = res[:limit] |
910 res = res[:limit] |
935 else: |
911 else: |
936 res = res.limit(limit) |
912 res = res.limit(limit) |
937 return res |
913 return res |
938 |
914 |
939 def set_related_cache(self, rtype, role, rset, col=0): |
915 def cw_set_relation_cache(self, rtype, role, rset): |
940 """set cached values for the given relation""" |
916 """set cached values for the given relation""" |
941 if rset: |
917 if rset: |
942 related = list(rset.entities(col)) |
918 related = list(rset.entities(0)) |
943 rschema = self._cw.vreg.schema.rschema(rtype) |
919 rschema = self._cw.vreg.schema.rschema(rtype) |
944 if role == 'subject': |
920 if role == 'subject': |
945 rcard = rschema.rdef(self.e_schema, related[0].e_schema).cardinality[1] |
921 rcard = rschema.rdef(self.e_schema, related[0].e_schema).cardinality[1] |
946 target = 'object' |
922 target = 'object' |
947 else: |
923 else: |
948 rcard = rschema.rdef(related[0].e_schema, self.e_schema).cardinality[0] |
924 rcard = rschema.rdef(related[0].e_schema, self.e_schema).cardinality[0] |
949 target = 'subject' |
925 target = 'subject' |
950 if rcard in '?1': |
926 if rcard in '?1': |
951 for rentity in related: |
927 for rentity in related: |
952 rentity._related_cache['%s_%s' % (rtype, target)] = ( |
928 rentity._cw_related_cache['%s_%s' % (rtype, target)] = ( |
953 self.as_rset(), (self,)) |
929 self.as_rset(), (self,)) |
954 else: |
930 else: |
955 related = () |
931 related = () |
956 self._related_cache['%s_%s' % (rtype, role)] = (rset, related) |
932 self._cw_related_cache['%s_%s' % (rtype, role)] = (rset, related) |
957 |
933 |
958 def clear_related_cache(self, rtype=None, role=None): |
934 def cw_clear_relation_cache(self, rtype=None, role=None): |
959 """clear cached values for the given relation or the entire cache if |
935 """clear cached values for the given relation or the entire cache if |
960 no relation is given |
936 no relation is given |
961 """ |
937 """ |
962 if rtype is None: |
938 if rtype is None: |
963 self._related_cache = {} |
939 self._cw_related_cache = {} |
964 else: |
940 else: |
965 assert role |
941 assert role |
966 self._related_cache.pop('%s_%s' % (rtype, role), None) |
942 self._cw_related_cache.pop('%s_%s' % (rtype, role), None) |
967 |
943 |
968 def clear_all_caches(self): |
944 def clear_all_caches(self): # XXX cw_clear_all_caches |
969 """flush all caches on this entity. Further attributes/relations access |
945 """flush all caches on this entity. Further attributes/relations access |
970 will triggers new database queries to get back values. |
946 will triggers new database queries to get back values. |
971 |
947 |
972 If you use custom caches on your entity class (take care to @cached!), |
948 If you use custom caches on your entity class (take care to @cached!), |
973 you should override this method to clear them as well. |
949 you should override this method to clear them as well. |
1031 values = (values,) |
1007 values = (values,) |
1032 self._cw.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( |
1008 self._cw.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( |
1033 restr, ','.join(str(r.eid) for r in values)), |
1009 restr, ','.join(str(r.eid) for r in values)), |
1034 {'x': self.eid}) |
1010 {'x': self.eid}) |
1035 |
1011 |
1036 def delete(self, **kwargs): |
1012 def cw_delete(self, **kwargs): |
1037 assert self.has_eid(), self.eid |
1013 assert self.has_eid(), self.eid |
1038 self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |
1014 self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |
1039 {'x': self.eid}, **kwargs) |
1015 {'x': self.eid}, **kwargs) |
1040 |
1016 |
1041 # server side utilities ################################################### |
1017 # server side utilities ################################################### |
1042 |
1018 |
|
1019 def _cw_rql_set_value(self, attr, value): |
|
1020 """call by rql execution plan when some attribute is modified |
|
1021 |
|
1022 don't use dict api in such case since we don't want attribute to be |
|
1023 added to skip_security_attributes. |
|
1024 |
|
1025 This method is for internal use, you should not use it. |
|
1026 """ |
|
1027 super(Entity, self).__setitem__(attr, value) |
|
1028 |
|
1029 def _cw_clear_local_perm_cache(self, action): |
|
1030 for rqlexpr in self.e_schema.get_rqlexprs(action): |
|
1031 self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) |
|
1032 |
1043 @property |
1033 @property |
1044 def skip_security_attributes(self): |
1034 def _cw_skip_security_attributes(self): |
1045 try: |
1035 try: |
1046 return self._skip_security_attributes |
1036 return self.__cw_skip_security_attributes |
1047 except: |
1037 except: |
1048 self._skip_security_attributes = set() |
1038 self.__cw_skip_security_attributes = set() |
1049 return self._skip_security_attributes |
1039 return self.__cw_skip_security_attributes |
1050 |
1040 |
1051 def set_defaults(self): |
1041 def _cw_set_defaults(self): |
1052 """set default values according to the schema""" |
1042 """set default values according to the schema""" |
1053 for attr, value in self.e_schema.defaults(): |
1043 for attr, value in self.e_schema.defaults(): |
1054 if not self.has_key(attr): |
1044 if not self.has_key(attr): |
1055 self[str(attr)] = value |
1045 self[str(attr)] = value |
1056 |
1046 |
1057 def check(self, creation=False): |
1047 def _cw_check(self, creation=False): |
1058 """check this entity against its schema. Only final relation |
1048 """check this entity against its schema. Only final relation |
1059 are checked here, constraint on actual relations are checked in hooks |
1049 are checked here, constraint on actual relations are checked in hooks |
1060 """ |
1050 """ |
1061 # necessary since eid is handled specifically and yams require it to be |
1051 # necessary since eid is handled specifically and yams require it to be |
1062 # in the dictionary |
1052 # in the dictionary |
1075 else: |
1065 else: |
1076 relations = None |
1066 relations = None |
1077 self.e_schema.check(self, creation=creation, _=_, |
1067 self.e_schema.check(self, creation=creation, _=_, |
1078 relations=relations) |
1068 relations=relations) |
1079 |
1069 |
|
1070 @deprecated('[3.9] use entity.cw_attr_value(attr)') |
|
1071 def get_value(self, name): |
|
1072 return self.cw_attr_value(name) |
|
1073 |
|
1074 @deprecated('[3.9] use entity.cw_delete()') |
|
1075 def delete(self, **kwargs): |
|
1076 return self.cw_delete(**kwargs) |
|
1077 |
|
1078 @deprecated('[3.9] use entity.cw_attr_metadata(attr, metadata)') |
|
1079 def attr_metadata(self, attr, metadata): |
|
1080 return self.cw_attr_metadata(attr, metadata) |
|
1081 |
|
1082 @deprecated('[3.9] use entity.cw_has_perm(action)') |
|
1083 def has_perm(self, action): |
|
1084 return self.cw_has_perm(action) |
|
1085 |
|
1086 @deprecated('[3.9] use entity.cw_set_relation_cache(rtype, role, rset)') |
|
1087 def set_related_cache(self, rtype, role, rset): |
|
1088 self.cw_set_relation_cache(rtype, role, rset) |
|
1089 |
|
1090 @deprecated('[3.9] use entity.cw_clear_relation_cache(rtype, role, rset)') |
|
1091 def clear_related_cache(self, rtype=None, role=None): |
|
1092 self.cw_clear_relation_cache(rtype, role) |
1080 |
1093 |
1081 # attribute and relation descriptors ########################################## |
1094 # attribute and relation descriptors ########################################## |
1082 |
1095 |
1083 class Attribute(object): |
1096 class Attribute(object): |
1084 """descriptor that controls schema attribute access""" |
1097 """descriptor that controls schema attribute access""" |