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

Source Code for Module ZenRelations.ToManyRelationship

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