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 Products.ZenUtils.Utils import getObjByPath
27
28 from ToManyRelationshipBase import ToManyRelationshipBase
29
30 from Products.ZenRelations.Exceptions import *
31
39
40
41 addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals())
42
43
45 """
46 ToManyRelationship manages the ToMany side of a bi-directional relation
47 between to objects. It does not return values for any of the object*
48 calls defined on ObjectManager so that Zope can still work with its
49 containment assumptions. It provides object*All calles that return
50 its object in the same way that ObjectManager does.
51
52 Related references are maintained in a list.
53 """
54
55 meta_type = "ToManyRelationship"
56
57 security = ClassSecurityInfo()
58
60 """ToManyRelationships use an array to store related objects"""
61 self.id = id
62 self._objects = []
63
64
66 """when we are called return our related object in our aq context"""
67 return [ob.__of__(self) for ob in self._objects]
68
69
71 "check to see if we have this object"
72 try:
73 idx = self._objects.index(obj)
74 return self._objects[idx]
75 except ValueError:
76 return None
77
78
80 """
81 there are 4 possible states for _operation during beforeDelete
82 -1 = object being deleted remove relation
83 0 = copy, 1 = move, 2 = rename
84 ToMany unlinks from its remote relations if its being deleted.
85 ToMany will not propagate beforeDelete because its not a container.
86 """
87 if getattr(item, "_operation", -1) < 1:
88 self._remoteRemove()
89
90
95
96
98 """add an object to one side of this toMany relationship"""
99 if obj in self._objects: raise RelationshipExistsError
100 self._objects.append(aq_base(obj))
101 self.__primary_parent__._p_changed = True
102
103
105 """remove object from our side of a relationship"""
106 if obj:
107 try:
108 self._objects.remove(obj)
109 except ValueError:
110 raise ObjectNotFound(
111 "Object with id %s not found on relation %s" %
112 (obj.id, self.id))
113 else:
114 self._objects = []
115 self.__primary_parent__._p_changed = True
116
117
119 """remove an object from the far side of this relationship
120 if no object is passed in remove all objects"""
121 if obj:
122 if obj not in self._objects: raise ObjectNotFound
123 objs = [obj]
124 else: objs = self.objectValuesAll()
125 remoteName = self.remoteName()
126 for obj in objs:
127 rel = getattr(obj, remoteName)
128 rel._remove(self.__primary_parent__)
129
130
132 """Set and object onto a ToMany by calling addRelation"""
133 self.addRelation(object)
134
135
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)
143
144
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
156 """
157 Return object ids as their absolute primaryId.
158 """
159 return [obj.getPrimaryId() for obj in self._objects]
160
161
163 """
164 ToManyRelationship doesn't publish objectIds to prevent
165 zope recursion problems.
166 """
167 return []
168
169
170 security.declareProtected('View', 'objectValuesAll')
172 """return all related object values"""
173 return [ob.__of__(self) for ob in self._objects]
174
175
177 """Generator that returns all related objects."""
178 for obj in self._objects:
179 yield obj.__of__(self)
180
181
183 """
184 ToManyRelationship doesn't publish objectValues to prevent
185 zope recursion problems.
186 """
187 return []
188
189
191 """
192 Return object items where key is primaryId.
193 """
194 objs = []
195 for obj in self._objects:
196 objs.append((obj.getPrimaryId(), obj))
197 return objs
198
199
201 """
202 ToManyRelationship doesn't publish objectItems to prevent
203 zope recursion problems.
204 """
205 return []
206
207
209 """
210 create copy and link remote objects if remote side is TO_MANY
211 """
212 rel = self.__class__(self.id)
213 rel.__primary_parent__ = container
214 rel = rel.__of__(container)
215 norelcopy = getattr(self, 'zNoRelationshipCopy', [])
216 if self.id in norelcopy: return rel
217 if self.remoteTypeName() == "ToMany":
218 for robj in self.objectValuesAll():
219 rel.addRelation(robj)
220 return rel
221
222
224 """Return an xml representation of a ToManyRelationship
225 <tomany id='interfaces'>
226 <link>/Systems/OOL/Mail</link>
227 </tomany>
228 """
229 if self.countObjects() == 0: return
230 ofile.write("<tomany id='%s'>\n" % self.id)
231 for id in self.objectIdsAll():
232 ofile.write("<link objid='%s'/>\n" % id)
233 ofile.write("</tomany>\n")
234
235
239
240
242 """Check to make sure that relationship bidirectionality is ok.
243 """
244 if len(self._objects): log.info("checking relation: %s", self.id)
245 rname = self.remoteName()
246 parobj = self.getPrimaryParent()
247
248
249
250 for obj in self._objects:
251 rrel = getattr(obj, rname)
252 if not rrel.hasobject(parobj):
253 log.critical("obj:%s rel:%s robj:%s rrel:%s doesn't point back",
254 parobj.getPrimaryId(), self.id, obj.getPrimaryId(),rname)
255 if repair:
256 log.warn("adding obj:%s to rrel:%s",
257 self.getPrimaryId(),rname)
258 rrel._add(parobj)
259 try:
260 ppath = obj.getPrimaryPath()
261 self.getObjByPath(ppath)
262 except KeyError:
263 log.critical("obj:%s rel:%s obj:%s no longer exists",
264 self.getPrimaryId(), self.id, obj.getPrimaryId())
265 if repair:
266 log.warn("removing rel to:%s", obj.getPrimaryId())
267 self._objects.remove(obj)
268 self.__primary_parent__._p_changed = True
269
270
271 keycount = {}
272 for obj in self._objects:
273 key = obj.getPrimaryId()
274 c = keycount.setdefault(key, 0)
275 c += 1
276 keycount[key] = c
277
278 for key, val in keycount.items():
279 if val > 1:
280 log.critical("obj:%s rel:%s dup found obj:%s count:%s",
281 self.getPrimaryId(), self.id, key, val)
282 if repair:
283 log.critical("repair key %s", key)
284 self._objects = [ o for o in self._objects \
285 if o.getPrimaryId() != key ]
286 try:
287 obj = self.getObjByPath(key)
288 self._objects.append(obj)
289 except KeyError:
290 log.critical("obj %s not found in database", key)
291
292
293 InitializeClass(ToManyRelationship)
294