Package Products :: Package ZenRelations :: Module ToManyRelationship
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenRelations.ToManyRelationship

  1  ############################################################################## 
  2  #  
  3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved. 
  4  #  
  5  # This content is made available according to terms specified in 
  6  # License.zenoss under the directory where your Zenoss product is installed. 
  7  #  
  8  ############################################################################## 
  9   
 10   
 11  __doc__="""$Id: ToManyRelationship.py,v 1.48 2003/11/12 22:05:48 edahl Exp $""" 
 12   
 13  __version__ = "$Revision: 1.48 $"[11:-2] 
 14   
 15  import sys 
 16  import logging 
 17  log = logging.getLogger("zen.Relations") 
 18   
 19  from Globals import DTMLFile 
 20  from Globals import InitializeClass 
 21  from AccessControl import ClassSecurityInfo 
 22  from Acquisition import aq_base 
 23   
 24  from zExceptions import NotFound 
 25  from Products.ZenUtils.Utils import getObjByPath, unused 
 26  from Products.ZenUtils.tbdetail import log_tb 
 27   
 28  from ToManyRelationshipBase import ToManyRelationshipBase 
 29   
 30  from Products.ZenRelations.Exceptions import * 
 31   
 32  from persistent.list import PersistentList 
 33   
34 -def manage_addToManyRelationship(context, id, REQUEST=None):
35 """factory for ToManyRelationship""" 36 rel = ToManyRelationship(id) 37 context._setObject(rel.id, rel) 38 if REQUEST: 39 REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main') 40 return rel.id
41 42 43 addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals()) 44 45
46 -class ToManyRelationship(ToManyRelationshipBase):
47 """ 48 ToManyRelationship manages the ToMany side of a bi-directional relation 49 between to objects. It does not return values for any of the object* 50 calls defined on ObjectManager so that Zope can still work with its 51 containment assumptions. It provides object*All calles that return 52 its object in the same way that ObjectManager does. 53 54 Related references are maintained in a list. 55 """ 56 57 __pychecker__='no-override' 58 59 meta_type = "ToManyRelationship" 60 61 security = ClassSecurityInfo() 62
63 - def __init__(self, id):
64 """ToManyRelationships use an array to store related objects""" 65 self.id = id 66 self._objects = PersistentList() 67 self._count = 0
68
69 - def __call__(self):
70 """when we are called return our related object in our aq context""" 71 return self.objectValuesAll()
72
73 - def hasobject(self, obj):
74 "check to see if we have this object" 75 try: 76 idx = self._objects.index(obj) 77 return self._objects[idx] 78 except ValueError: 79 return None
80 81
82 - def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
83 """ToManyRelationships link instead of pasting""" 84 return self.manage_linkObjects(cb_copy_data=cb_copy_data, 85 REQUEST=REQUEST)
86 87
88 - def _add(self,obj):
89 """add an object to one side of this toMany relationship""" 90 if obj in self._objects: raise RelationshipExistsError 91 self._objects.append(aq_base(obj)) 92 self.__primary_parent__._p_changed = True 93 self.setCount()
94 95
96 - def _remove(self, obj=None, suppress_events=False):
97 """remove object from our side of a relationship""" 98 if obj: 99 try: 100 self._objects.remove(obj) 101 except ValueError: 102 raise ObjectNotFound( 103 "object %s not found on relation %s" % ( 104 obj.getPrimaryId(), self.getPrimaryId())) 105 else: 106 self._objects = PersistentList() 107 self.__primary_parent__._p_changed = True 108 self.setCount()
109 110
111 - def _remoteRemove(self, obj=None):
112 """remove an object from the far side of this relationship 113 if no object is passed in remove all objects""" 114 if obj: 115 if obj not in self._objects: 116 raise ObjectNotFound("object %s not found on relation %s" % ( 117 obj.getPrimaryId(), self.getPrimaryId())) 118 objs = [obj] 119 else: objs = self.objectValuesAll() 120 remoteName = self.remoteName() 121 for obj in objs: 122 rel = getattr(obj, remoteName) 123 try: 124 rel._remove(self.__primary_parent__) 125 except ObjectNotFound: 126 message = log_tb(sys.exc_info()) 127 log.error('Remote remove failed. Run "zenchkrels -r -x1". ' + message)
128 129
130 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
131 """Set and object onto a ToMany by calling addRelation""" 132 unused(id, roles, user, set_owner) 133 self.addRelation(object)
134 135
136 - def _delObject(self, id, dp=1, suppress_events=False):
137 """ 138 Delete object by its absolute id (ie /zport/dmd/bla/bla) 139 (this is sent out in the object*All API) 140 """ 141 obj = getObjByPath(self, id) 142 self.removeRelation(obj, suppress_events=suppress_events)
143 144
145 - def _getOb(self, id, default=zenmarker):
146 """ 147 Return object based on its primaryId. plain id will not work!!! 148 """ 149 objs = filter(lambda x: x.getPrimaryId() == id, self._objects) 150 if len(objs) == 1: return objs[0].__of__(self) 151 if default != zenmarker: return default 152 raise AttributeError(id)
153 154
155 - def objectIdsAll(self):
156 """ 157 Return object ids as their absolute primaryId. 158 """ 159 return [obj.getPrimaryId() for obj in self._objects]
160 161
162 - def objectIds(self, spec=None):
163 """ 164 ToManyRelationship doesn't publish objectIds to prevent 165 zope recursion problems. 166 """ 167 unused(spec) 168 return []
169 170 171 security.declareProtected('View', 'objectValuesAll')
172 - def objectValuesAll(self):
173 """return all related object values""" 174 return list(self.objectValuesGen())
175 176
177 - def objectValuesGen(self):
178 """Generator that returns all related objects.""" 179 rname = self.remoteName() 180 parobj = self.getPrimaryParent() 181 for obj in self._objects: 182 # Disabling relationship checking code. 183 # http://dev.zenoss.org/trac/ticket/5391 184 #if self.checkObjectRelation(obj, rname, parobj, True): 185 # continue 186 yield obj.__of__(self)
187 188
189 - def objectValues(self, spec=None):
190 """ 191 ToManyRelationship doesn't publish objectValues to prevent 192 zope recursion problems. 193 """ 194 unused(spec) 195 return []
196 197
198 - def objectItemsAll(self):
199 """ 200 Return object items where key is primaryId. 201 """ 202 objs = [] 203 for obj in self._objects: 204 objs.append((obj.getPrimaryId(), obj)) 205 return objs
206 207
208 - def objectItems(self, spec=None):
209 """ 210 ToManyRelationship doesn't publish objectItems to prevent 211 zope recursion problems. 212 """ 213 unused(spec) 214 return []
215 216
217 - def _getCopy(self, container):
218 """ 219 create copy and link remote objects if remote side is TO_MANY 220 """ 221 rel = self.__class__(self.id) 222 rel.__primary_parent__ = container 223 rel = rel.__of__(container) 224 norelcopy = getattr(self, 'zNoRelationshipCopy', []) 225 if self.id in norelcopy: return rel 226 if self.remoteTypeName() == "ToMany": 227 for robj in self.objectValuesAll(): 228 rel.addRelation(robj) 229 return rel
230 231
232 - def exportXml(self,ofile,ignorerels=[]):
233 """Return an xml representation of a ToManyRelationship 234 <tomany id='interfaces'> 235 <link>/Systems/OOL/Mail</link> 236 </tomany> 237 """ 238 if self.countObjects() == 0: return 239 ofile.write("<tomany id='%s'>\n" % self.id) 240 for id in self.objectIdsAll(): 241 ofile.write("<link objid='%s'/>\n" % id) 242 ofile.write("</tomany>\n")
243 244
245 - def all_meta_types(self, interfaces=None):
246 """Return empty list not allowed to add objects to a ToManyRelation""" 247 return []
248 249
250 - def convertToPersistentList(self):
251 self._objects = PersistentList(self._objects) 252 self.setCount()
253 254
255 - def checkObjectRelation(self, obj, remoteName, parentObject, repair):
256 deleted = False 257 try: 258 ppath = obj.getPrimaryPath() 259 getObjByPath(self, ppath) 260 except (KeyError, NotFound): 261 log.error("object %s in relation %s has been deleted " \ 262 "from its primary path", 263 obj.getPrimaryId(), self.getPrimaryId()) 264 if repair: 265 log.warn("removing object %s from relation %s", 266 obj.getPrimaryId(), self.getPrimaryId()) 267 self._objects.remove(obj) 268 self.__primary_parent__._p_changed = True 269 deleted = True 270 271 if not deleted: 272 rrel = getattr(obj, remoteName) 273 if not rrel.hasobject(parentObject): 274 log.error("remote relation %s doesn't point back to %s", 275 rrel.getPrimaryId(), self.getPrimaryId()) 276 if repair: 277 log.warn("reconnecting relation %s to relation %s", 278 rrel.getPrimaryId(),self.getPrimaryId()) 279 rrel._add(parentObject) 280 return deleted
281 282
283 - def checkRelation(self, repair=False):
284 """Check to make sure that relationship bidirectionality is ok. 285 """ 286 if len(self._objects): 287 log.debug("checking relation: %s", self.id) 288 289 # look for objects that don't point back to us 290 # or who should no longer exist in the database 291 rname = self.remoteName() 292 parobj = self.getPrimaryParent() 293 for obj in self._objects: 294 self.checkObjectRelation(obj, rname, parobj, repair) 295 296 # find duplicate objects 297 keycount = {} 298 for obj in self._objects: 299 key = obj.getPrimaryId() 300 c = keycount.setdefault(key, 0) 301 c += 1 302 keycount[key] = c 303 # Remove duplicate objects or objects that don't exist 304 for key, val in keycount.items(): 305 if val > 1: 306 log.critical("obj:%s rel:%s dup found obj:%s count:%s", 307 self.getPrimaryId(), self.id, key, val) 308 if repair: 309 log.critical("repair key %s", key) 310 self._objects = [ o for o in self._objects \ 311 if o.getPrimaryId() != key ] 312 try: 313 obj = self.getObjByPath(key) 314 self._objects.append(obj) 315 except KeyError: 316 log.critical("obj %s not found in database", key)
317 318 319 InitializeClass(ToManyRelationship) 320