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, unused
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 __pychecker__='no-override'
56
57 meta_type = "ToManyRelationship"
58
59 security = ClassSecurityInfo()
60
62 """ToManyRelationships use an array to store related objects"""
63 self.id = id
64 self._objects = []
65
66
68 """when we are called return our related object in our aq context"""
69 return [ob.__of__(self) for ob in self._objects]
70
71
73 "check to see if we have this object"
74 try:
75 idx = self._objects.index(obj)
76 return self._objects[idx]
77 except ValueError:
78 return None
79
80
82 """
83 there are 4 possible states for _operation during beforeDelete
84 -1 = object being deleted remove relation
85 0 = copy, 1 = move, 2 = rename
86 ToMany unlinks from its remote relations if its being deleted.
87 ToMany will not propagate beforeDelete because its not a container.
88 """
89 unused(container)
90 if getattr(item, "_operation", -1) < 1:
91 self._remoteRemove()
92
93
98
99
100 - def _add(self,obj):
101 """add an object to one side of this toMany relationship"""
102 if obj in self._objects: raise RelationshipExistsError
103 self._objects.append(aq_base(obj))
104 self.__primary_parent__._p_changed = True
105
106
108 """remove object from our side of a relationship"""
109 if obj:
110 try:
111 self._objects.remove(obj)
112 except ValueError:
113 raise ObjectNotFound(
114 "Object with id %s not found on relation %s" %
115 (obj.id, self.id))
116 else:
117 self._objects = []
118 self.__primary_parent__._p_changed = True
119
120
122 """remove an object from the far side of this relationship
123 if no object is passed in remove all objects"""
124 if obj:
125 if obj not in self._objects: raise ObjectNotFound
126 objs = [obj]
127 else: objs = self.objectValuesAll()
128 remoteName = self.remoteName()
129 for obj in objs:
130 rel = getattr(obj, remoteName)
131 rel._remove(self.__primary_parent__)
132
133
134 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
135 """Set and object onto a ToMany by calling addRelation"""
136 unused(id, roles, user, set_owner)
137 self.addRelation(object)
138
139
141 """
142 Delete object by its absolute id (ie /zport/dmd/bla/bla)
143 (this is sent out in the object*All API)
144 """
145 obj = getObjByPath(self, id)
146 self.removeRelation(obj)
147
148
150 """
151 Return object based on its primaryId. plain id will not work!!!
152 """
153 objs = filter(lambda x: x.getPrimaryId() == id, self._objects)
154 if len(objs) == 1: return objs[0].__of__(self)
155 if default != zenmarker: return default
156 raise AttributeError(id)
157
158
160 """
161 Return object ids as their absolute primaryId.
162 """
163 return [obj.getPrimaryId() for obj in self._objects]
164
165
167 """
168 ToManyRelationship doesn't publish objectIds to prevent
169 zope recursion problems.
170 """
171 unused(spec)
172 return []
173
174
175 security.declareProtected('View', 'objectValuesAll')
177 """return all related object values"""
178 return [ob.__of__(self) for ob in self._objects]
179
180
182 """Generator that returns all related objects."""
183 for obj in self._objects:
184 yield obj.__of__(self)
185
186
188 """
189 ToManyRelationship doesn't publish objectValues to prevent
190 zope recursion problems.
191 """
192 unused(spec)
193 return []
194
195
197 """
198 Return object items where key is primaryId.
199 """
200 objs = []
201 for obj in self._objects:
202 objs.append((obj.getPrimaryId(), obj))
203 return objs
204
205
207 """
208 ToManyRelationship doesn't publish objectItems to prevent
209 zope recursion problems.
210 """
211 unused(spec)
212 return []
213
214
216 """
217 create copy and link remote objects if remote side is TO_MANY
218 """
219 rel = self.__class__(self.id)
220 rel.__primary_parent__ = container
221 rel = rel.__of__(container)
222 norelcopy = getattr(self, 'zNoRelationshipCopy', [])
223 if self.id in norelcopy: return rel
224 if self.remoteTypeName() == "ToMany":
225 for robj in self.objectValuesAll():
226 rel.addRelation(robj)
227 return rel
228
229
231 """Return an xml representation of a ToManyRelationship
232 <tomany id='interfaces'>
233 <link>/Systems/OOL/Mail</link>
234 </tomany>
235 """
236 if self.countObjects() == 0: return
237 ofile.write("<tomany id='%s'>\n" % self.id)
238 for id in self.objectIdsAll():
239 ofile.write("<link objid='%s'/>\n" % id)
240 ofile.write("</tomany>\n")
241
242
246
247
249 """Check to make sure that relationship bidirectionality is ok.
250 """
251 if len(self._objects): log.info("checking relation: %s", self.id)
252 rname = self.remoteName()
253 parobj = self.getPrimaryParent()
254
255
256
257 for obj in self._objects:
258 rrel = getattr(obj, rname)
259 if not rrel.hasobject(parobj):
260 log.critical("obj:%s rel:%s robj:%s rrel:%s doesn't point back",
261 parobj.getPrimaryId(), self.id, obj.getPrimaryId(),rname)
262 if repair:
263 log.warn("adding obj:%s to rrel:%s",
264 self.getPrimaryId(),rname)
265 rrel._add(parobj)
266 try:
267 ppath = obj.getPrimaryPath()
268 self.getObjByPath(ppath)
269 except KeyError:
270 log.critical("obj:%s rel:%s obj:%s no longer exists",
271 self.getPrimaryId(), self.id, obj.getPrimaryId())
272 if repair:
273 log.warn("removing rel to:%s", obj.getPrimaryId())
274 self._objects.remove(obj)
275 self.__primary_parent__._p_changed = True
276
277
278 keycount = {}
279 for obj in self._objects:
280 key = obj.getPrimaryId()
281 c = keycount.setdefault(key, 0)
282 c += 1
283 keycount[key] = c
284
285 for key, val in keycount.items():
286 if val > 1:
287 log.critical("obj:%s rel:%s dup found obj:%s count:%s",
288 self.getPrimaryId(), self.id, key, val)
289 if repair:
290 log.critical("repair key %s", key)
291 self._objects = [ o for o in self._objects \
292 if o.getPrimaryId() != key ]
293 try:
294 obj = self.getObjByPath(key)
295 self._objects.append(obj)
296 except KeyError:
297 log.critical("obj %s not found in database", key)
298
299
300 InitializeClass(ToManyRelationship)
301