1
2
3
4
5
6
7
8
9
10
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 from persistent.list import PersistentList
34
42
43
44 addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals())
45
46
48 """
49 ToManyRelationship manages the ToMany side of a bi-directional relation
50 between to objects. It does not return values for any of the object*
51 calls defined on ObjectManager so that Zope can still work with its
52 containment assumptions. It provides object*All calles that return
53 its object in the same way that ObjectManager does.
54
55 Related references are maintained in a list.
56 """
57
58 __pychecker__='no-override'
59
60 meta_type = "ToManyRelationship"
61
62 security = ClassSecurityInfo()
63
65 """ToManyRelationships use an array to store related objects"""
66 self.id = id
67 self._objects = PersistentList()
68 self._count = 0
69
71 """when we are called return our related object in our aq context"""
72 return self.objectValuesAll()
73
75 "check to see if we have this object"
76 try:
77 idx = self._objects.index(obj)
78 return self._objects[idx]
79 except ValueError:
80 return None
81
82
87
88
90 """add an object to one side of this toMany relationship"""
91 if obj in self._objects: raise RelationshipExistsError
92 self._objects.append(aq_base(obj))
93 self.__primary_parent__._p_changed = True
94 self.setCount()
95
96
97 - def _remove(self, obj=None, suppress_events=False):
98 """remove object from our side of a relationship"""
99 if obj:
100 try:
101 self._objects.remove(obj)
102 except ValueError:
103 raise ObjectNotFound(
104 "object %s not found on relation %s" % (
105 obj.getPrimaryId(), self.getPrimaryId()))
106 else:
107 self._objects = PersistentList()
108 self.__primary_parent__._p_changed = True
109 self.setCount()
110
111
113 """remove an object from the far side of this relationship
114 if no object is passed in remove all objects"""
115 if obj:
116 if obj not in self._objects:
117 raise ObjectNotFound("object %s not found on relation %s" % (
118 obj.getPrimaryId(), self.getPrimaryId()))
119 objs = [obj]
120 else: objs = self.objectValuesAll()
121 remoteName = self.remoteName()
122 for obj in objs:
123 rel = getattr(obj, remoteName)
124 rel._remove(self.__primary_parent__)
125
126
127 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
128 """Set and object onto a ToMany by calling addRelation"""
129 unused(id, roles, user, set_owner)
130 self.addRelation(object)
131
132
133 - def _delObject(self, id, dp=1, suppress_events=False):
134 """
135 Delete object by its absolute id (ie /zport/dmd/bla/bla)
136 (this is sent out in the object*All API)
137 """
138 obj = getObjByPath(self, id)
139 self.removeRelation(obj, suppress_events=suppress_events)
140
141
143 """
144 Return object based on its primaryId. plain id will not work!!!
145 """
146 objs = filter(lambda x: x.getPrimaryId() == id, self._objects)
147 if len(objs) == 1: return objs[0].__of__(self)
148 if default != zenmarker: return default
149 raise AttributeError(id)
150
151
153 """
154 Return object ids as their absolute primaryId.
155 """
156 return [obj.getPrimaryId() for obj in self._objects]
157
158
160 """
161 ToManyRelationship doesn't publish objectIds to prevent
162 zope recursion problems.
163 """
164 unused(spec)
165 return []
166
167
168 security.declareProtected('View', 'objectValuesAll')
172
173
175 """Generator that returns all related objects."""
176 rname = self.remoteName()
177 parobj = self.getPrimaryParent()
178 for obj in self._objects:
179
180
181
182
183 yield obj.__of__(self)
184
185
187 """
188 ToManyRelationship doesn't publish objectValues to prevent
189 zope recursion problems.
190 """
191 unused(spec)
192 return []
193
194
196 """
197 Return object items where key is primaryId.
198 """
199 objs = []
200 for obj in self._objects:
201 objs.append((obj.getPrimaryId(), obj))
202 return objs
203
204
206 """
207 ToManyRelationship doesn't publish objectItems to prevent
208 zope recursion problems.
209 """
210 unused(spec)
211 return []
212
213
215 """
216 create copy and link remote objects if remote side is TO_MANY
217 """
218 rel = self.__class__(self.id)
219 rel.__primary_parent__ = container
220 rel = rel.__of__(container)
221 norelcopy = getattr(self, 'zNoRelationshipCopy', [])
222 if self.id in norelcopy: return rel
223 if self.remoteTypeName() == "ToMany":
224 for robj in self.objectValuesAll():
225 rel.addRelation(robj)
226 return rel
227
228
230 """Return an xml representation of a ToManyRelationship
231 <tomany id='interfaces'>
232 <link>/Systems/OOL/Mail</link>
233 </tomany>
234 """
235 if self.countObjects() == 0: return
236 ofile.write("<tomany id='%s'>\n" % self.id)
237 for id in self.objectIdsAll():
238 ofile.write("<link objid='%s'/>\n" % id)
239 ofile.write("</tomany>\n")
240
241
245
246
248 self._objects = PersistentList(self._objects)
249 self.setCount()
250
251
253 deleted = False
254 try:
255 ppath = obj.getPrimaryPath()
256 getObjByPath(self, ppath)
257 except (KeyError, NotFound):
258 log.error("object %s in relation %s has been deleted " \
259 "from its primary path",
260 obj.getPrimaryId(), self.getPrimaryId())
261 if repair:
262 log.warn("removing object %s from relation %s",
263 obj.getPrimaryId(), self.getPrimaryId())
264 self._objects.remove(obj)
265 self.__primary_parent__._p_changed = True
266 deleted = True
267
268 if not deleted:
269 rrel = getattr(obj, remoteName)
270 if not rrel.hasobject(parentObject):
271 log.error("remote relation %s doesn't point back to %s",
272 rrel.getPrimaryId(), self.getPrimaryId())
273 if repair:
274 log.warn("reconnecting relation %s to relation %s",
275 rrel.getPrimaryId(),self.getPrimaryId())
276 rrel._add(parentObject)
277 return deleted
278
279
281 """Check to make sure that relationship bidirectionality is ok.
282 """
283 if len(self._objects):
284 log.debug("checking relation: %s", self.id)
285
286
287
288 rname = self.remoteName()
289 parobj = self.getPrimaryParent()
290 for obj in self._objects:
291 self.checkObjectRelation(obj, rname, parobj, repair)
292
293
294 keycount = {}
295 for obj in self._objects:
296 key = obj.getPrimaryId()
297 c = keycount.setdefault(key, 0)
298 c += 1
299 keycount[key] = c
300
301 for key, val in keycount.items():
302 if val > 1:
303 log.critical("obj:%s rel:%s dup found obj:%s count:%s",
304 self.getPrimaryId(), self.id, key, val)
305 if repair:
306 log.critical("repair key %s", key)
307 self._objects = [ o for o in self._objects \
308 if o.getPrimaryId() != key ]
309 try:
310 obj = self.getObjByPath(key)
311 self._objects.append(obj)
312 except KeyError:
313 log.critical("obj %s not found in database", key)
314
315
316 InitializeClass(ToManyRelationship)
317