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 as published by
      8  # 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 -def manage_addToManyRelationship(context, id, REQUEST=None):
     34      """factory for ToManyRelationship"""
     35      rel =  ToManyRelationship(id)
     36      context._setObject(rel.id, rel)
     37      if REQUEST:
     38          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     39      return rel.id
     40
     41
     42  addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals())
     43
     44
     45 -class ToManyRelationship(ToManyRelationshipBase):
     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
     62 -    def __init__(self, id):
     63          """ToManyRelationships use an array to store related objects"""
     64          self.id = id
     65          self._objects = []
     66
     67
     68 -    def __call__(self):
     69          """when we are called return our related object in our aq context"""
     70          return self.objectValuesAll()
     71
     72
     73 -    def hasobject(self, obj):
     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
     82 -    def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
     83          """ToManyRelationships link instead of pasting"""
     84          return self.manage_linkObjects(cb_copy_data=cb_copy_data,
     85                                          REQUEST=REQUEST)
     86
     87
     88 -    def _add(self,obj):
     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
     95 -    def _remove(self, obj=None):
     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
    109 -    def _remoteRemove(self, obj=None):
    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
    130 -    def _delObject(self, id):
    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
    139 -    def _getOb(self, id, default=zenmarker):
    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
    149 -    def objectIdsAll(self):
    150          """
    151          Return object ids as their absolute primaryId.
    152          """
    153          return [obj.getPrimaryId() for obj in self._objects]
    154
    155
    156 -    def objectIds(self, spec=None):
    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')
    166 -    def objectValuesAll(self):
    167          """return all related object values"""
    168          return list(self.objectValuesGen())
    169
    170
    171 -    def objectValuesGen(self):
    172          """Generator that returns all related objects."""
    173          rname = self.remoteName()
    174          parobj = self.getPrimaryParent()
    175          for obj in self._objects:
    176              # Disabling relationship checking code.
    177              # http://dev.zenoss.org/trac/ticket/5391
    178              #if self.checkObjectRelation(obj, rname, parobj, True):
    179              #    continue
    180              yield obj.__of__(self)
    181
    182
    183 -    def objectValues(self, spec=None):
    184          """
    185          ToManyRelationship doesn't publish objectValues to prevent
    186          zope recursion problems.
    187          """
    188          unused(spec)
    189          return []
    190
    191
    192 -    def objectItemsAll(self):
    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
    202 -    def objectItems(self, spec=None):
    203          """
    204          ToManyRelationship doesn't publish objectItems to prevent
    205          zope recursion problems.
    206          """
    207          unused(spec)
    208          return []
    209
    210
    211 -    def _getCopy(self, container):
    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
    226 -    def exportXml(self,ofile,ignorerels=[]):
    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
    239 -    def all_meta_types(self, interfaces=None):
    240          """Return empty list not allowed to add objects to a ToManyRelation"""
    241          return []
    242
    243
    244 -    def checkObjectRelation(self, obj, remoteName, parentObject, repair):
    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
    272 -    def checkRelation(self, repair=False):
    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          # look for objects that don't point back to us
    279          # or who should no longer exist in the database
    280          rname = self.remoteName()
    281          parobj = self.getPrimaryParent()
    282          for obj in self._objects:
    283              self.checkObjectRelation(obj, rname, parobj, repair)
    284
    285          # find duplicate objects
    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          # Remove duplicate objects or objects that don't exist
    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