entity.py
changeset 3890 d7a270f50f54
parent 3777 3ef8cdb5fb1c
parent 3877 7ca53fc72a0a
child 3998 94cc7cad3d2d
equal deleted inserted replaced
3810:5b75fd66c80e 3890:d7a270f50f54
    32 _marker = object()
    32 _marker = object()
    33 
    33 
    34 def greater_card(rschema, subjtypes, objtypes, index):
    34 def greater_card(rschema, subjtypes, objtypes, index):
    35     for subjtype in subjtypes:
    35     for subjtype in subjtypes:
    36         for objtype in objtypes:
    36         for objtype in objtypes:
    37             card = rschema.rproperty(subjtype, objtype, 'cardinality')[index]
    37             card = rschema.rdef(subjtype, objtype).cardinality[index]
    38             if card in '+*':
    38             if card in '+*':
    39                 return card
    39                 return card
    40     return '1'
    40     return '1'
    41 
    41 
    42 
    42 
   142                 rschema = eschema.subjrels[attr]
   142                 rschema = eschema.subjrels[attr]
   143             except KeyError:
   143             except KeyError:
   144                 cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
   144                 cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
   145                             attr, cls.__regid__)
   145                             attr, cls.__regid__)
   146                 continue
   146                 continue
   147             if not user.matching_groups(rschema.get_groups('read')):
   147             rdef = eschema.rdef(attr)
       
   148             if not user.matching_groups(rdef.get_groups('read')):
   148                 continue
   149                 continue
   149             var = varmaker.next()
   150             var = varmaker.next()
   150             selection.append(var)
   151             selection.append(var)
   151             restriction = '%s %s %s' % (mainvar, attr, var)
   152             restriction = '%s %s %s' % (mainvar, attr, var)
   152             restrictions.append(restriction)
   153             restrictions.append(restriction)
   153             if not rschema.final:
   154             if not rschema.final:
   154                 # XXX this does not handle several destination types
   155                 # XXX this does not handle several destination types
   155                 desttype = rschema.objects(eschema.type)[0]
   156                 desttype = rschema.objects(eschema.type)[0]
   156                 card = rschema.rproperty(eschema, desttype, 'cardinality')[0]
   157                 card = rdef.cardinality[0]
   157                 if card not in '?1':
   158                 if card not in '?1':
   158                     cls.warning('bad relation %s specified in fetch attrs for %s',
   159                     cls.warning('bad relation %s specified in fetch attrs for %s',
   159                                  attr, cls)
   160                                  attr, cls)
   160                     selection.pop()
   161                     selection.pop()
   161                     restrictions.pop()
   162                     restrictions.pop()
   254     def clear_local_perm_cache(self, action):
   255     def clear_local_perm_cache(self, action):
   255         for rqlexpr in self.e_schema.get_rqlexprs(action):
   256         for rqlexpr in self.e_schema.get_rqlexprs(action):
   256             self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None)
   257             self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None)
   257 
   258 
   258     def check_perm(self, action):
   259     def check_perm(self, action):
   259         self.e_schema.check_perm(self._cw, action, self.eid)
   260         self.e_schema.check_perm(self._cw, action, eid=self.eid)
   260 
   261 
   261     def has_perm(self, action):
   262     def has_perm(self, action):
   262         return self.e_schema.has_perm(self._cw, action, self.eid)
   263         return self.e_schema.has_perm(self._cw, action, eid=self.eid)
   263 
   264 
   264     def view(self, vid, __registry='views', **kwargs):
   265     def view(self, vid, __registry='views', **kwargs):
   265         """shortcut to apply a view on this entity"""
   266         """shortcut to apply a view on this entity"""
   266         view = self._cw.vreg[__registry].select(vid, self._cw, rset=self.cw_rset,
   267         view = self._cw.vreg[__registry].select(vid, self._cw, rset=self.cw_rset,
   267                                                 row=self.cw_row, col=self.cw_col,
   268                                                 row=self.cw_row, col=self.cw_col,
   337             value = value.strip()
   338             value = value.strip()
   338         if value is None or value == '': # don't use "not", 0 is an acceptable value
   339         if value is None or value == '': # don't use "not", 0 is an acceptable value
   339             return u''
   340             return u''
   340         if attrtype is None:
   341         if attrtype is None:
   341             attrtype = self.e_schema.destination(attr)
   342             attrtype = self.e_schema.destination(attr)
   342         props = self.e_schema.rproperties(attr)
   343         props = self.e_schema.rdef(attr)
   343         if attrtype == 'String':
   344         if attrtype == 'String':
   344             # internalinalized *and* formatted string such as schema
   345             # internalinalized *and* formatted string such as schema
   345             # description...
   346             # description...
   346             if props.get('internationalizable'):
   347             if props.internationalizable:
   347                 value = self._cw._(value)
   348                 value = self._cw._(value)
   348             attrformat = self.attr_metadata(attr, 'format')
   349             attrformat = self.attr_metadata(attr, 'format')
   349             if attrformat:
   350             if attrformat:
   350                 return self.mtc_transform(value, attrformat, format,
   351                 return self.mtc_transform(value, attrformat, format,
   351                                           self._cw.encoding)
   352                                           self._cw.encoding)
   389             if getattr(self, rschema.type):
   390             if getattr(self, rschema.type):
   390                 continue
   391                 continue
   391             if rschema.type in self.skip_copy_for:
   392             if rschema.type in self.skip_copy_for:
   392                 continue
   393                 continue
   393             # skip composite relation
   394             # skip composite relation
   394             if self.e_schema.subjrproperty(rschema, 'composite'):
   395             rdef = self.e_schema.rdef(rschema)
       
   396             if rdef.composite:
   395                 continue
   397                 continue
   396             # skip relation with card in ?1 else we either change the copied
   398             # skip relation with card in ?1 else we either change the copied
   397             # object (inlined relation) or inserting some inconsistency
   399             # object (inlined relation) or inserting some inconsistency
   398             if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
   400             if rdef.cardinality[1] in '?1':
   399                 continue
   401                 continue
   400             rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
   402             rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
   401                 rschema.type, rschema.type)
   403                 rschema.type, rschema.type)
   402             execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
   404             execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
   403             self.clear_related_cache(rschema.type, 'subject')
   405             self.clear_related_cache(rschema.type, 'subject')
   404         for rschema in self.e_schema.object_relations():
   406         for rschema in self.e_schema.object_relations():
   405             if rschema.meta:
   407             if rschema.meta:
   406                 continue
   408                 continue
   407             # skip already defined relations
   409             # skip already defined relations
   408             if getattr(self, 'reverse_%s' % rschema.type):
   410             if self.related(rschema.type, 'object'):
   409                 continue
   411                 continue
       
   412             rdef = self.e_schema.rdef(rschema, 'object')
   410             # skip composite relation
   413             # skip composite relation
   411             if self.e_schema.objrproperty(rschema, 'composite'):
   414             if rdef.composite:
   412                 continue
   415                 continue
   413             # skip relation with card in ?1 else we either change the copied
   416             # skip relation with card in ?1 else we either change the copied
   414             # object (inlined relation) or inserting some inconsistency
   417             # object (inlined relation) or inserting some inconsistency
   415             if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1':
   418             if rdef.cardinality[0] in '?1':
   416                 continue
   419                 continue
   417             rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
   420             rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
   418                 rschema.type, rschema.type)
   421                 rschema.type, rschema.type)
   419             execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
   422             execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
   420             self.clear_related_cache(rschema.type, 'object')
   423             self.clear_related_cache(rschema.type, 'object')
   431     def to_complete_relations(self):
   434     def to_complete_relations(self):
   432         """by default complete final relations to when calling .complete()"""
   435         """by default complete final relations to when calling .complete()"""
   433         for rschema in self.e_schema.subject_relations():
   436         for rschema in self.e_schema.subject_relations():
   434             if rschema.final:
   437             if rschema.final:
   435                 continue
   438                 continue
   436             if len(rschema.objects(self.e_schema)) > 1:
   439             targets = rschema.objects(self.e_schema)
       
   440             if len(targets) > 1:
   437                 # ambigous relations, the querier doesn't handle
   441                 # ambigous relations, the querier doesn't handle
   438                 # outer join correctly in this case
   442                 # outer join correctly in this case
   439                 continue
   443                 continue
   440             if rschema.inlined:
   444             if rschema.inlined:
   441                 matching_groups = self._cw.user.matching_groups
   445                 matching_groups = self._cw.user.matching_groups
   442                 if matching_groups(rschema.get_groups('read')) and \
   446                 rdef = rschema.rdef(self.e_schema, targets[0])
   443                    all(matching_groups(es.get_groups('read'))
   447                 if matching_groups(rdef.get_groups('read')) and \
   444                        for es in rschema.objects(self.e_schema)):
   448                    all(matching_groups(e.get_groups('read')) for e in targets):
   445                     yield rschema, 'subject'
   449                     yield rschema, 'subject'
   446 
   450 
   447     def to_complete_attributes(self, skip_bytes=True):
   451     def to_complete_attributes(self, skip_bytes=True):
   448         for rschema, attrschema in self.e_schema.attribute_definitions():
   452         for rschema, attrschema in self.e_schema.attribute_definitions():
   449             # skip binary data by default
   453             # skip binary data by default
   451                 continue
   455                 continue
   452             attr = rschema.type
   456             attr = rschema.type
   453             if attr == 'eid':
   457             if attr == 'eid':
   454                 continue
   458                 continue
   455             # password retreival is blocked at the repository server level
   459             # password retreival is blocked at the repository server level
   456             if not self._cw.user.matching_groups(rschema.get_groups('read')) \
   460             rdef = rschema.rdef(self.e_schema, attrschema)
       
   461             if not self._cw.user.matching_groups(rdef.get_groups('read')) \
   457                    or attrschema.type == 'Password':
   462                    or attrschema.type == 'Password':
   458                 self[attr] = None
   463                 self[attr] = None
   459                 continue
   464                 continue
   460             yield attr
   465             yield attr
   461 
   466 
   487             for rschema, role in self.to_complete_relations():
   492             for rschema, role in self.to_complete_relations():
   488                 rtype = rschema.type
   493                 rtype = rschema.type
   489                 if self.relation_cached(rtype, role):
   494                 if self.relation_cached(rtype, role):
   490                     continue
   495                     continue
   491                 var = varmaker.next()
   496                 var = varmaker.next()
       
   497                 targettype = rschema.targets(self.e_schema, role)[0]
       
   498                 rdef = rschema.role_rdef(self.e_schema, targettype, role)
       
   499                 card = rdef.role_cardinality(role)
       
   500                 assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype,
       
   501                                                       role, card)
   492                 if role == 'subject':
   502                 if role == 'subject':
   493                     targettype = rschema.objects(self.e_schema)[0]
       
   494                     card = rschema.rproperty(self.e_schema, targettype,
       
   495                                              'cardinality')[0]
       
   496                     if card == '1':
   503                     if card == '1':
   497                         rql.append('%s %s %s' % (V, rtype, var))
   504                         rql.append('%s %s %s' % (V, rtype, var))
   498                     else: # '?"
   505                     else:
   499                         rql.append('%s %s %s?' % (V, rtype, var))
   506                         rql.append('%s %s %s?' % (V, rtype, var))
   500                 else:
   507                 else:
   501                     targettype = rschema.subjects(self.e_schema)[1]
       
   502                     card = rschema.rproperty(self.e_schema, targettype,
       
   503                                              'cardinality')[1]
       
   504                     if card == '1':
   508                     if card == '1':
   505                         rql.append('%s %s %s' % (var, rtype, V))
   509                         rql.append('%s %s %s' % (var, rtype, V))
   506                     else: # '?"
   510                     else:
   507                         rql.append('%s? %s %s' % (var, rtype, V))
   511                         rql.append('%s? %s %s' % (var, rtype, V))
   508                 assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype,
       
   509                                                       role, card)
       
   510                 selected.append(((rtype, role), var))
   512                 selected.append(((rtype, role), var))
   511         if selected:
   513         if selected:
   512             # select V, we need it as the left most selected variable
   514             # select V, we need it as the left most selected variable
   513             # if some outer join are included to fetch inlined relations
   515             # if some outer join are included to fetch inlined relations
   514             rql = 'Any %s,%s %s' % (V, ','.join(var for attr, var in selected),
   516             rql = 'Any %s,%s %s' % (V, ','.join(var for attr, var in selected),
   650                 securitycheck_args = {'toeid': self.eid}
   652                 securitycheck_args = {'toeid': self.eid}
   651         else:
   653         else:
   652             restriction = []
   654             restriction = []
   653             args = {}
   655             args = {}
   654             securitycheck_args = {}
   656             securitycheck_args = {}
   655         insertsecurity = (rtype.has_local_role('add') and not
   657         rdef = rtype.role_rdef(self.e_schema, targettype, role)
   656                           rtype.has_perm(self._cw, 'add', **securitycheck_args))
   658         insertsecurity = (rdef.has_local_role('add') and not
   657         constraints = rtype.rproperty(subjtype, objtype, 'constraints')
   659                           rdef.has_perm(self._cw, 'add', **securitycheck_args))
   658         if vocabconstraints:
   660         if vocabconstraints:
   659             # RQLConstraint is a subclass for RQLVocabularyConstraint, so they
   661             # RQLConstraint is a subclass for RQLVocabularyConstraint, so they
   660             # will be included as well
   662             # will be included as well
   661             restriction += [cstr.restriction for cstr in constraints
   663             restriction += [cstr.restriction for cstr in rdef.constraints
   662                             if isinstance(cstr, RQLVocabularyConstraint)]
   664                             if isinstance(cstr, RQLVocabularyConstraint)]
   663         else:
   665         else:
   664             restriction += [cstr.restriction for cstr in constraints
   666             restriction += [cstr.restriction for cstr in rdef.constraints
   665                             if isinstance(cstr, RQLConstraint)]
   667                             if isinstance(cstr, RQLConstraint)]
   666         etypecls = self._cw.vreg['etypes'].etype_class(targettype)
   668         etypecls = self._cw.vreg['etypes'].etype_class(targettype)
   667         rql = etypecls.fetch_rql(self._cw.user, restriction,
   669         rql = etypecls.fetch_rql(self._cw.user, restriction,
   668                                  mainvar=searchedvar, ordermethod=ordermethod)
   670                                  mainvar=searchedvar, ordermethod=ordermethod)
   669         # ensure we have an order defined
   671         # ensure we have an order defined
   670         if not ' ORDERBY ' in rql:
   672         if not ' ORDERBY ' in rql:
   671             before, after = rql.split(' WHERE ', 1)
   673             before, after = rql.split(' WHERE ', 1)
   672             rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
   674             rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
   673         if insertsecurity:
   675         if insertsecurity:
   674             rqlexprs = rtype.get_rqlexprs('add')
   676             rqlexprs = rdef.get_rqlexprs('add')
   675             rewriter = RQLRewriter(self._cw)
   677             rewriter = RQLRewriter(self._cw)
   676             rqlst = self._cw.vreg.parse(self._cw, rql, args)
   678             rqlst = self._cw.vreg.parse(self._cw, rql, args)
       
   679             if not self.has_eid():
       
   680                 existant = searchedvar
       
   681             else:
       
   682                 existant = None # instead of 'SO', improve perfs
   677             for select in rqlst.children:
   683             for select in rqlst.children:
   678                 rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)],
   684                 rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)],
   679                                  select.solutions, args)
   685                                  select.solutions, args, existant)
   680             rql = rqlst.as_string()
   686             rql = rqlst.as_string()
   681         return rql, args
   687         return rql, args
   682 
   688 
   683     def unrelated(self, rtype, targettype, role='subject', limit=None,
   689     def unrelated(self, rtype, targettype, role='subject', limit=None,
   684                   ordermethod=None):
   690                   ordermethod=None):
   717         """set cached values for the given relation"""
   723         """set cached values for the given relation"""
   718         if rset:
   724         if rset:
   719             related = list(rset.entities(col))
   725             related = list(rset.entities(col))
   720             rschema = self._cw.vreg.schema.rschema(rtype)
   726             rschema = self._cw.vreg.schema.rschema(rtype)
   721             if role == 'subject':
   727             if role == 'subject':
   722                 rcard = rschema.rproperty(self.e_schema, related[0].e_schema,
   728                 rcard = rschema.rdef(self.e_schema, related[0].e_schema).cardinality[1]
   723                                           'cardinality')[1]
       
   724                 target = 'object'
   729                 target = 'object'
   725             else:
   730             else:
   726                 rcard = rschema.rproperty(related[0].e_schema, self.e_schema,
   731                 rcard = rschema.rdef(related[0].e_schema, self.e_schema).cardinality[0]
   727                                           'cardinality')[0]
       
   728                 target = 'subject'
   732                 target = 'subject'
   729             if rcard in '?1':
   733             if rcard in '?1':
   730                 for rentity in related:
   734                 for rentity in related:
   731                     rentity._related_cache['%s_%s' % (rtype, target)] = (
   735                     rentity._related_cache['%s_%s' % (rtype, target)] = (
   732                         self.as_rset(), (self,))
   736                         self.as_rset(), (self,))