entity.py
changeset 8735 5567a5117aeb
parent 8705 5b3e17a63fad
child 8748 f5027f8d2478
child 8980 c0b82dbcf6a3
equal deleted inserted replaced
8734:3530b7494195 8735:5567a5117aeb
   995         :param safe:
   995         :param safe:
   996           if True, an empty rset/list of entities will be returned in case of
   996           if True, an empty rset/list of entities will be returned in case of
   997           :exc:`Unauthorized`, else (the default), the exception is propagated
   997           :exc:`Unauthorized`, else (the default), the exception is propagated
   998         """
   998         """
   999         rtype = str(rtype)
   999         rtype = str(rtype)
  1000         try:
  1000         if limit is None:
  1001             return self._cw_relation_cache(rtype, role, entities, limit)
  1001             # we cannot do much wrt cache on limited queries
  1002         except KeyError:
  1002             cache_key = '%s_%s' % (rtype, role)
  1003             pass
  1003             if cache_key in self._cw_related_cache:
       
  1004                 return self._cw_related_cache[cache_key][entities]
  1004         if not self.has_eid():
  1005         if not self.has_eid():
  1005             if entities:
  1006             if entities:
  1006                 return []
  1007                 return []
  1007             return self._cw.empty_rset()
  1008             return self._cw.empty_rset()
  1008         rql = self.cw_related_rql(rtype, role)
  1009         rql = self.cw_related_rql(rtype, role, limit=limit)
  1009         try:
  1010         try:
  1010             rset = self._cw.execute(rql, {'x': self.eid})
  1011             rset = self._cw.execute(rql, {'x': self.eid})
  1011         except Unauthorized:
  1012         except Unauthorized:
  1012             if not safe:
  1013             if not safe:
  1013                 raise
  1014                 raise
  1014             rset = self._cw.empty_rset()
  1015             rset = self._cw.empty_rset()
  1015         self.cw_set_relation_cache(rtype, role, rset)
  1016         if entities:
  1016         return self.related(rtype, role, limit, entities)
  1017             if limit is None:
  1017 
  1018                 self.cw_set_relation_cache(rtype, role, rset)
  1018     def cw_related_rql(self, rtype, role='subject', targettypes=None):
  1019                 return self.related(rtype, role, limit, entities)
       
  1020             return list(rset.entities())
       
  1021         else:
       
  1022             return rset
       
  1023 
       
  1024     def cw_related_rql(self, rtype, role='subject', targettypes=None, limit=None):
  1019         vreg = self._cw.vreg
  1025         vreg = self._cw.vreg
  1020         rschema = vreg.schema[rtype]
  1026         rschema = vreg.schema[rtype]
  1021         select = Select()
  1027         select = Select()
  1022         mainvar, evar = select.get_variable('X'), select.get_variable('E')
  1028         mainvar, evar = select.get_variable('X'), select.get_variable('E')
  1023         select.add_selected(mainvar)
  1029         select.add_selected(mainvar)
       
  1030         if limit is not None:
       
  1031             select.set_limit(limit)
  1024         select.add_eid_restriction(evar, 'x', 'Substitute')
  1032         select.add_eid_restriction(evar, 'x', 'Substitute')
  1025         if role == 'subject':
  1033         if role == 'subject':
  1026             rel = make_relation(evar, rtype, (mainvar,), VariableRef)
  1034             rel = make_relation(evar, rtype, (mainvar,), VariableRef)
  1027             select.add_restriction(rel)
  1035             select.add_restriction(rel)
  1028             if targettypes is None:
  1036             if targettypes is None:
  1073         return select.as_string()
  1081         return select.as_string()
  1074 
  1082 
  1075     # generic vocabulary methods ##############################################
  1083     # generic vocabulary methods ##############################################
  1076 
  1084 
  1077     def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None,
  1085     def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None,
  1078                          vocabconstraints=True, lt_infos={}):
  1086                          vocabconstraints=True, lt_infos={}, limit=None):
  1079         """build a rql to fetch `targettype` entities unrelated to this entity
  1087         """build a rql to fetch `targettype` entities unrelated to this entity
  1080         using (rtype, role) relation.
  1088         using (rtype, role) relation.
  1081 
  1089 
  1082         Consider relation permissions so that returned entities may be actually
  1090         Consider relation permissions so that returned entities may be actually
  1083         linked by `rtype`.
  1091         linked by `rtype`.
  1102             searchedvar = objvar = select.get_variable('O')
  1110             searchedvar = objvar = select.get_variable('O')
  1103         else:
  1111         else:
  1104             searchedvar = subjvar = select.get_variable('S')
  1112             searchedvar = subjvar = select.get_variable('S')
  1105             evar = objvar = select.get_variable('O')
  1113             evar = objvar = select.get_variable('O')
  1106         select.add_selected(searchedvar)
  1114         select.add_selected(searchedvar)
       
  1115         if limit is not None:
       
  1116             select.set_limit(limit)
  1107         # initialize some variables according to `self` existence
  1117         # initialize some variables according to `self` existence
  1108         if rdef.role_cardinality(neg_role(role)) in '?1':
  1118         if rdef.role_cardinality(neg_role(role)) in '?1':
  1109             # if cardinality in '1?', we want a target entity which isn't
  1119             # if cardinality in '1?', we want a target entity which isn't
  1110             # already linked using this relation
  1120             # already linked using this relation
  1111             variable = select.make_variable()
  1121             variable = select.make_variable()
  1195                   ordermethod=None, lt_infos={}): # XXX .cw_unrelated
  1205                   ordermethod=None, lt_infos={}): # XXX .cw_unrelated
  1196         """return a result set of target type objects that may be related
  1206         """return a result set of target type objects that may be related
  1197         by a given relation, with self as subject or object
  1207         by a given relation, with self as subject or object
  1198         """
  1208         """
  1199         try:
  1209         try:
  1200             rql, args = self.cw_unrelated_rql(rtype, targettype, role,
  1210             rql, args = self.cw_unrelated_rql(rtype, targettype, role, limit=limit,
  1201                                               ordermethod, lt_infos=lt_infos)
  1211                                               ordermethod=ordermethod, lt_infos=lt_infos)
  1202         except Unauthorized:
  1212         except Unauthorized:
  1203             return self._cw.empty_rset()
  1213             return self._cw.empty_rset()
  1204         # XXX should be set in unrelated rql when manipulating the AST
       
  1205         if limit is not None:
       
  1206             before, after = rql.split(' WHERE ', 1)
       
  1207             rql = '%s LIMIT %s WHERE %s' % (before, limit, after)
       
  1208         return self._cw.execute(rql, args)
  1214         return self._cw.execute(rql, args)
  1209 
  1215 
  1210     # relations cache handling #################################################
  1216     # relations cache handling #################################################
  1211 
  1217 
  1212     def cw_relation_cached(self, rtype, role):
  1218     def cw_relation_cached(self, rtype, role):
  1213         """return None if the given relation isn't already cached on the
  1219         """return None if the given relation isn't already cached on the
  1214         instance, else the content of the cache (a 2-uple (rset, entities)).
  1220         instance, else the content of the cache (a 2-uple (rset, entities)).
  1215         """
  1221         """
  1216         return self._cw_related_cache.get('%s_%s' % (rtype, role))
  1222         return self._cw_related_cache.get('%s_%s' % (rtype, role))
  1217 
       
  1218     def _cw_relation_cache(self, rtype, role, entities=True, limit=None):
       
  1219         """return values for the given relation if it's cached on the instance,
       
  1220         else raise `KeyError`
       
  1221         """
       
  1222         res = self._cw_related_cache['%s_%s' % (rtype, role)][entities]
       
  1223         if limit is not None and limit < len(res):
       
  1224             if entities:
       
  1225                 res = res[:limit]
       
  1226             else:
       
  1227                 res = res.limit(limit)
       
  1228         return res
       
  1229 
  1223 
  1230     def cw_set_relation_cache(self, rtype, role, rset):
  1224     def cw_set_relation_cache(self, rtype, role, rset):
  1231         """set cached values for the given relation"""
  1225         """set cached values for the given relation"""
  1232         if rset:
  1226         if rset:
  1233             related = list(rset.entities(0))
  1227             related = list(rset.entities(0))