Package Products :: Package ZenRelations :: Module ToManyRelationship |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenRelations.ToManyRelationship

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 or (at your
      8  # option) any later version as published by the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     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
     35 -def manage_addToManyRelationship(context, id, REQUEST=None):
     36      """factory for ToManyRelationship"""
     37      rel =  ToManyRelationship(id)
     38      context._setObject(rel.id, rel)
     39      if REQUEST:
     40          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     41      return rel.id
     42
     43
     44  addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals())
     45
     46
     47 -class ToManyRelationship(ToManyRelationshipBase):
     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
     64 -    def __init__(self, id):
     65          """ToManyRelationships use an array to store related objects"""
     66          self.id = id
     67          self._objects = PersistentList()
     68          self._count = 0
     69
     70 -    def __call__(self):
     71          """when we are called return our related object in our aq context"""
     72          return self.objectValuesAll()
     73
     74 -    def hasobject(self, obj):
     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
     83 -    def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
     84          """ToManyRelationships link instead of pasting"""
     85          return self.manage_linkObjects(cb_copy_data=cb_copy_data,
     86                                          REQUEST=REQUEST)
     87
     88
     89 -    def _add(self,obj):
     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
    112 -    def _remoteRemove(self, obj=None):
    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
    142 -    def _getOb(self, id, default=zenmarker):
    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
    152 -    def objectIdsAll(self):
    153          """
    154          Return object ids as their absolute primaryId.
    155          """
    156          return [obj.getPrimaryId() for obj in self._objects]
    157
    158
    159 -    def objectIds(self, spec=None):
    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')
    169 -    def objectValuesAll(self):
    170          """return all related object values"""
    171          return list(self.objectValuesGen())
    172
    173
    174 -    def objectValuesGen(self):
    175          """Generator that returns all related objects."""
    176          rname = self.remoteName()
    177          parobj = self.getPrimaryParent()
    178          for obj in self._objects:
    179              # Disabling relationship checking code.
    180              # http://dev.zenoss.org/trac/ticket/5391
    181              #if self.checkObjectRelation(obj, rname, parobj, True):
    182              #    continue
    183              yield obj.__of__(self)
    184
    185
    186 -    def objectValues(self, spec=None):
    187          """
    188          ToManyRelationship doesn't publish objectValues to prevent
    189          zope recursion problems.
    190          """
    191          unused(spec)
    192          return []
    193
    194
    195 -    def objectItemsAll(self):
    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
    205 -    def objectItems(self, spec=None):
    206          """
    207          ToManyRelationship doesn't publish objectItems to prevent
    208          zope recursion problems.
    209          """
    210          unused(spec)
    211          return []
    212
    213
    214 -    def _getCopy(self, container):
    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
    229 -    def exportXml(self,ofile,ignorerels=[]):
    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
    242 -    def all_meta_types(self, interfaces=None):
    243          """Return empty list not allowed to add objects to a ToManyRelation"""
    244          return []
    245
    246
    247 -    def convertToPersistentList(self):
    248          self._objects = PersistentList(self._objects)
    249          self.setCount()
    250
    251
    252 -    def checkObjectRelation(self, obj, remoteName, parentObject, repair):
    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
    280 -    def checkRelation(self, repair=False):
    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          # look for objects that don't point back to us
    287          # or who should no longer exist in the database
    288          rname = self.remoteName()
    289          parobj = self.getPrimaryParent()
    290          for obj in self._objects:
    291              self.checkObjectRelation(obj, rname, parobj, repair)
    292
    293          # find duplicate objects
    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          # Remove duplicate objects or objects that don't exist
    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