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
40
41
42 addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals())
43
44
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
63 """ToManyRelationships use an array to store related objects"""
64 self.id = id
65 self._objects = []
66
67
69 """when we are called return our related object in our aq context"""
70 return self.objectValuesAll()
71
72
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
86
87
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
94
96 """remove object from our side of a relationship"""
97 if obj:
98 try:
99 self._objects.remove(obj)
100 except ValueError:
101 raise ObjectNotFound(
102 "object %s not found on relation %s" % (
103 obj.getPrimaryId(), self.getPrimaryId()))
104 else:
105 self._objects = []
106 self.__primary_parent__._p_changed = True
107
108
110 """remove an object from the far side of this relationship
111 if no object is passed in remove all objects"""
112 if obj:
113 if obj not in self._objects:
114 raise ObjectNotFound("object %s not found on relation %s" % (
115 obj.getPrimaryId(), self.getPrimaryId()))
116 objs = [obj]
117 else: objs = self.objectValuesAll()
118 remoteName = self.remoteName()
119 for obj in objs:
120 rel = getattr(obj, remoteName)
121 rel._remove(self.__primary_parent__)
122
123
124 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
125 """Set and object onto a ToMany by calling addRelation"""
126 unused(id, roles, user, set_owner)
127 self.addRelation(object)
128
129
131 """
132 Delete object by its absolute id (ie /zport/dmd/bla/bla)
133 (this is sent out in the object*All API)
134 """
135 obj = getObjByPath(self, id)
136 self.removeRelation(obj)
137
138
140 """
141 Return object based on its primaryId. plain id will not work!!!
142 """
143 objs = filter(lambda x: x.getPrimaryId() == id, self._objects)
144 if len(objs) == 1: return objs[0].__of__(self)
145 if default != zenmarker: return default
146 raise AttributeError(id)
147
148
150 """
151 Return object ids as their absolute primaryId.
152 """
153 return [obj.getPrimaryId() for obj in self._objects]
154
155
157 """
158 ToManyRelationship doesn't publish objectIds to prevent
159 zope recursion problems.
160 """
161 unused(spec)
162 return []
163
164
165 security.declareProtected('View', 'objectValuesAll')
169
170
172 """Generator that returns all related objects."""
173 rname = self.remoteName()
174 parobj = self.getPrimaryParent()
175 for obj in self._objects:
176
177
178
179
180 yield obj.__of__(self)
181
182
184 """
185 ToManyRelationship doesn't publish objectValues to prevent
186 zope recursion problems.
187 """
188 unused(spec)
189 return []
190
191
193 """
194 Return object items where key is primaryId.
195 """
196 objs = []
197 for obj in self._objects:
198 objs.append((obj.getPrimaryId(), obj))
199 return objs
200
201
203 """
204 ToManyRelationship doesn't publish objectItems to prevent
205 zope recursion problems.
206 """
207 unused(spec)
208 return []
209
210
212 """
213 create copy and link remote objects if remote side is TO_MANY
214 """
215 rel = self.__class__(self.id)
216 rel.__primary_parent__ = container
217 rel = rel.__of__(container)
218 norelcopy = getattr(self, 'zNoRelationshipCopy', [])
219 if self.id in norelcopy: return rel
220 if self.remoteTypeName() == "ToMany":
221 for robj in self.objectValuesAll():
222 rel.addRelation(robj)
223 return rel
224
225
227 """Return an xml representation of a ToManyRelationship
228 <tomany id='interfaces'>
229 <link>/Systems/OOL/Mail</link>
230 </tomany>
231 """
232 if self.countObjects() == 0: return
233 ofile.write("<tomany id='%s'>\n" % self.id)
234 for id in self.objectIdsAll():
235 ofile.write("<link objid='%s'/>\n" % id)
236 ofile.write("</tomany>\n")
237
238
242
243
245 deleted = False
246 try:
247 ppath = obj.getPrimaryPath()
248 getObjByPath(self, ppath)
249 except (KeyError, NotFound):
250 log.error("object %s in relation %s has been deleted " \
251 "from its primary path",
252 obj.getPrimaryId(), self.getPrimaryId())
253 if repair:
254 log.warn("removing object %s from relation %s",
255 obj.getPrimaryId(), self.getPrimaryId())
256 self._objects.remove(obj)
257 self.__primary_parent__._p_changed = True
258 deleted = True
259
260 if not deleted:
261 rrel = getattr(obj, remoteName)
262 if not rrel.hasobject(parentObject):
263 log.error("remote relation %s doesn't point back to %s",
264 rrel.getPrimaryId(), self.getPrimaryId())
265 if repair:
266 log.warn("reconnecting relation %s to relation %s",
267 rrel.getPrimaryId(),self.getPrimaryId())
268 rrel._add(parentObject)
269 return deleted
270
271
273 """Check to make sure that relationship bidirectionality is ok.
274 """
275 if len(self._objects):
276 log.debug("checking relation: %s", self.id)
277
278
279
280 rname = self.remoteName()
281 parobj = self.getPrimaryParent()
282 for obj in self._objects:
283 self.checkObjectRelation(obj, rname, parobj, repair)
284
285
286 keycount = {}
287 for obj in self._objects:
288 key = obj.getPrimaryId()
289 c = keycount.setdefault(key, 0)
290 c += 1
291 keycount[key] = c
292
293 for key, val in keycount.items():
294 if val > 1:
295 log.critical("obj:%s rel:%s dup found obj:%s count:%s",
296 self.getPrimaryId(), self.id, key, val)
297 if repair:
298 log.critical("repair key %s", key)
299 self._objects = [ o for o in self._objects \
300 if o.getPrimaryId() != key ]
301 try:
302 obj = self.getObjByPath(key)
303 self._objects.append(obj)
304 except KeyError:
305 log.critical("obj %s not found in database", key)
306
307
308 InitializeClass(ToManyRelationship)
309