server/edition.py
branchstable
changeset 7123 bb303290a6cb
child 7132 e9c92bb79787
equal deleted inserted replaced
7122:3b0f1c2af20f 7123:bb303290a6cb
       
     1 from copy import copy
       
     2 
       
     3 _MARKER = object()
       
     4 
       
     5 class dict_protocol_catcher(object):
       
     6     def __init__(self, entity):
       
     7         self.__entity = entity
       
     8     def __getitem__(self, attr):
       
     9         return self.__entity.cw_edited[attr]
       
    10     def __setitem__(self, attr, value):
       
    11         self.__entity.cw_edited[attr] = value
       
    12     def __getattr__(self, attr):
       
    13         return getattr(self.__entity, attr)
       
    14 
       
    15 
       
    16 class EditedEntity(dict):
       
    17     """encapsulate entities attributes being written by an RQL query"""
       
    18     def __init__(self, entity, **kwargs):
       
    19         dict.__init__(self, **kwargs)
       
    20         self.entity = entity
       
    21         self.skip_security = set()
       
    22         self.querier_pending_relations = {}
       
    23         self.saved = False
       
    24 
       
    25     def __hash__(self):
       
    26         # dict|set keyable
       
    27         return hash(id(self))
       
    28 
       
    29     def __cmp__(self, other):
       
    30         # we don't want comparison by value inherited from dict
       
    31         return cmp(id(self), id(other))
       
    32 
       
    33     def __setitem__(self, attr, value):
       
    34         assert attr != 'eid'
       
    35         # don't add attribute into skip_security if already in edited
       
    36         # attributes, else we may accidentaly skip a desired security check
       
    37         if attr not in self:
       
    38             self.skip_security.add(attr)
       
    39         self.edited_attribute(attr, value)
       
    40 
       
    41     def __delitem__(self, attr):
       
    42         assert not self.saved, 'too late to modify edited attributes'
       
    43         super(EditedEntity, self).__delitem__(attr)
       
    44         self.entity.cw_attr_cache.pop(attr, None)
       
    45 
       
    46     def pop(self, attr, *args):
       
    47         # don't update skip_security by design (think to storage api)
       
    48         assert not self.saved, 'too late to modify edited attributes'
       
    49         value = super(EditedEntity, self).pop(attr, *args)
       
    50         self.entity.cw_attr_cache.pop(attr, *args)
       
    51         return value
       
    52 
       
    53     def setdefault(self, attr, default):
       
    54         assert attr != 'eid'
       
    55         # don't add attribute into skip_security if already in edited
       
    56         # attributes, else we may accidentaly skip a desired security check
       
    57         if attr not in self:
       
    58             self[attr] = default
       
    59         return self[attr]
       
    60 
       
    61     def update(self, values, skipsec=True):
       
    62         if skipsec:
       
    63             setitem = self.__setitem__
       
    64         else:
       
    65             setitem = self.edited_attribute
       
    66         for attr, value in values.iteritems():
       
    67             setitem(attr, value)
       
    68 
       
    69     def edited_attribute(self, attr, value):
       
    70         """attribute being edited by a rql query: should'nt be added to
       
    71         skip_security
       
    72         """
       
    73         assert not self.saved, 'too late to modify edited attributes'
       
    74         super(EditedEntity, self).__setitem__(attr, value)
       
    75         self.entity.cw_attr_cache[attr] = value
       
    76 
       
    77     def oldnewvalue(self, attr):
       
    78         """returns the couple (old attr value, new attr value)
       
    79 
       
    80         NOTE: will only work in a before_update_entity hook
       
    81         """
       
    82         assert not self.saved, 'too late to get the old value'
       
    83         # get new value and remove from local dict to force a db query to
       
    84         # fetch old value
       
    85         newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER)
       
    86         oldvalue = getattr(self.entity, attr)
       
    87         if newvalue is not _MARKER:
       
    88             self.entity.cw_attr_cache[attr] = newvalue
       
    89         else:
       
    90             newvalue = oldvalue
       
    91         return oldvalue, newvalue
       
    92 
       
    93     def set_defaults(self):
       
    94         """set default values according to the schema"""
       
    95         for attr, value in self.entity.e_schema.defaults():
       
    96             if not attr in self:
       
    97                 self[str(attr)] = value
       
    98 
       
    99     def check(self, creation=False):
       
   100         """check the entity edition against its schema. Only final relation
       
   101         are checked here, constraint on actual relations are checked in hooks
       
   102         """
       
   103         entity = self.entity
       
   104         if creation:
       
   105             # on creations, we want to check all relations, especially
       
   106             # required attributes
       
   107             relations = [rschema for rschema in entity.e_schema.subject_relations()
       
   108                          if rschema.final and rschema.type != 'eid']
       
   109         else:
       
   110             relations = [entity._cw.vreg.schema.rschema(rtype)
       
   111                          for rtype in self]
       
   112         from yams import ValidationError
       
   113         try:
       
   114             entity.e_schema.check(dict_protocol_catcher(entity),
       
   115                                   creation=creation, _=entity._cw._,
       
   116                                   relations=relations)
       
   117         except ValidationError, ex:
       
   118             ex.entity = self.entity
       
   119             raise
       
   120 
       
   121     def clone(self):
       
   122         thecopy = EditedEntity(copy(self.entity))
       
   123         thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache)
       
   124         thecopy.entity._cw_related_cache = {}
       
   125         thecopy.update(self, skipsec=False)
       
   126         return thecopy
       
   127 
       
   128