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
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
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
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
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
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
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
163 """
164 Return object ids as their absolute primaryId.
165 """
166 return [obj.getPrimaryId() for obj in self._objects]
167
168
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')
182
183
185 """Generator that returns all related objects."""
186 rname = self.remoteName()
187 parobj = self.getPrimaryParent()
188 for obj in self._objects:
189
190
191
192
193 yield obj.__of__(self)
194
195
197 """
198 ToManyRelationship doesn't publish objectValues to prevent
199 zope recursion problems.
200 """
201 unused(spec)
202 return []
203
204
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
216 """
217 ToManyRelationship doesn't publish objectItems to prevent
218 zope recursion problems.
219 """
220 unused(spec)
221 return []
222
223
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
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
255
256
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
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
292
293 rname = self.remoteName()
294 parobj = self.getPrimaryParent()
295 for obj in self._objects:
296 self.checkObjectRelation(obj, rname, parobj, repair)
297
298
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
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