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

# Source Code for Module Products.ZenRelations.ToManyRelationship

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  __doc__="""$Id: ToManyRelationship.py,v 1.48 2003/11/12 22:05:48 edahl Exp $"""
     12
     13  __version__ = "$Revision: 1.48 $"[11:-2]
     14
     15  import sys
     16  import logging
     17  log = logging.getLogger("zen.Relations")
     18
     19  from Globals import DTMLFile
     20  from Globals import InitializeClass
     21  from AccessControl import ClassSecurityInfo
     22  from Acquisition import aq_base
     23
     24  from zExceptions import NotFound
     25  from Products.ZenUtils.Utils import getObjByPath, unused
     26  from Products.ZenUtils.tbdetail import log_tb
     27
     28  from ToManyRelationshipBase import ToManyRelationshipBase
     29
     30  from Products.ZenRelations.Exceptions import *
     31
     32  from persistent.list import PersistentList
     33
     34 -def manage_addToManyRelationship(context, id, REQUEST=None):
     35      """factory for ToManyRelationship"""
     36      rel =  ToManyRelationship(id)
     37      context._setObject(rel.id, rel)
     38      if REQUEST:
     39          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     40      return rel.id
     41
     42
     43  addToManyRelationship = DTMLFile('dtml/addToManyRelationship',globals())
     44
     45
     46 -class ToManyRelationship(ToManyRelationshipBase):
     47      """
     48      ToManyRelationship manages the ToMany side of a bi-directional relation
     49      between to objects.  It does not return values for any of the object*
     50      calls defined on ObjectManager so that Zope can still work with its
     51      containment assumptions.  It provides object*All calles that return
     52      its object in the same way that ObjectManager does.
     53
     54      Related references are maintained in a list.
     55      """
     56
     57      __pychecker__='no-override'
     58
     59      meta_type = "ToManyRelationship"
     60
     61      security = ClassSecurityInfo()
     62
     63 -    def __init__(self, id):
     64          """ToManyRelationships use an array to store related objects"""
     65          self.id = id
     66          self._objects = PersistentList()
     67          self._count = 0
     68
     69 -    def __call__(self):
     70          """when we are called return our related object in our aq context"""
     71          return self.objectValuesAll()
     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          self.setCount()
     94
     95
     96 -    def _remove(self, obj=None, suppress_events=False):
     97          """remove object from our side of a relationship"""
     98          if obj:
     99              try:
    100                  self._objects.remove(obj)
    101              except ValueError:
    102                  raise ObjectNotFound(
    103                      "object %s not found on relation %s" % (
    104                          obj.getPrimaryId(), self.getPrimaryId()))
    105          else:
    106              self._objects = PersistentList()
    107          self.__primary_parent__._p_changed = True
    108          self.setCount()
    109
    110
    111 -    def _remoteRemove(self, obj=None):
    112          """remove an object from the far side of this relationship
    113          if no object is passed in remove all objects"""
    114          if obj:
    115              if obj not in self._objects:
    116                  raise ObjectNotFound("object %s not found on relation %s" % (
    117                              obj.getPrimaryId(), self.getPrimaryId()))
    118              objs = [obj]
    119          else: objs = self.objectValuesAll()
    120          remoteName = self.remoteName()
    121          for obj in objs:
    122              rel = getattr(obj, remoteName)
    123              try:
    124                  rel._remove(self.__primary_parent__)
    125              except ObjectNotFound:
    126                  message = log_tb(sys.exc_info())
    127                  log.error('Remote remove failed. Run "zenchkrels -r -x1". ' + message)
    128
    129
    130 -    def _setObject(self,id,object,roles=None,user=None,set_owner=1):
    131          """Set and object onto a ToMany by calling addRelation"""
    132          unused(id, roles, user, set_owner)
    133          self.addRelation(object)
    134
    135
    136 -    def _delObject(self, id, dp=1, suppress_events=False):
    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, suppress_events=suppress_events)
    143
    144
    145 -    def _getOb(self, id, default=zenmarker):
    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
    155 -    def objectIdsAll(self):
    156          """
    157          Return object ids as their absolute primaryId.
    158          """
    159          return [obj.getPrimaryId() for obj in self._objects]
    160
    161
    162 -    def objectIds(self, spec=None):
    163          """
    164          ToManyRelationship doesn't publish objectIds to prevent
    165          zope recursion problems.
    166          """
    167          unused(spec)
    168          return []
    169
    170
    171      security.declareProtected('View', 'objectValuesAll')
    172 -    def objectValuesAll(self):
    173          """return all related object values"""
    174          return list(self.objectValuesGen())
    175
    176
    177 -    def objectValuesGen(self):
    178          """Generator that returns all related objects."""
    179          rname = self.remoteName()
    180          parobj = self.getPrimaryParent()
    181          for obj in self._objects:
    182              # Disabling relationship checking code.
    183              # http://dev.zenoss.org/trac/ticket/5391
    184              #if self.checkObjectRelation(obj, rname, parobj, True):
    185              #    continue
    186              yield obj.__of__(self)
    187
    188
    189 -    def objectValues(self, spec=None):
    190          """
    191          ToManyRelationship doesn't publish objectValues to prevent
    192          zope recursion problems.
    193          """
    194          unused(spec)
    195          return []
    196
    197
    198 -    def objectItemsAll(self):
    199          """
    200          Return object items where key is primaryId.
    201          """
    202          objs = []
    203          for obj in self._objects:
    204              objs.append((obj.getPrimaryId(), obj))
    205          return objs
    206
    207
    208 -    def objectItems(self, spec=None):
    209          """
    210          ToManyRelationship doesn't publish objectItems to prevent
    211          zope recursion problems.
    212          """
    213          unused(spec)
    214          return []
    215
    216
    217 -    def _getCopy(self, container):
    218          """
    219          create copy and link remote objects if remote side is TO_MANY
    220          """
    221          rel = self.__class__(self.id)
    222          rel.__primary_parent__ = container
    223          rel = rel.__of__(container)
    224          norelcopy = getattr(self, 'zNoRelationshipCopy', [])
    225          if self.id in norelcopy: return rel
    226          if self.remoteTypeName() == "ToMany":
    227              for robj in self.objectValuesAll():
    228                  rel.addRelation(robj)
    229          return rel
    230
    231
    232 -    def exportXml(self,ofile,ignorerels=[]):
    233          """Return an xml representation of a ToManyRelationship
    234          <tomany id='interfaces'>
    235              <link>/Systems/OOL/Mail</link>
    236          </tomany>
    237          """
    238          if self.countObjects() == 0: return
    239          ofile.write("<tomany id='%s'>\n" % self.id)
    240          for id in self.objectIdsAll():
    241              ofile.write("<link objid='%s'/>\n" % id)
    242          ofile.write("</tomany>\n")
    243
    244
    245 -    def all_meta_types(self, interfaces=None):
    246          """Return empty list not allowed to add objects to a ToManyRelation"""
    247          return []
    248
    249
    250 -    def convertToPersistentList(self):
    251          self._objects = PersistentList(self._objects)
    252          self.setCount()
    253
    254
    255 -    def checkObjectRelation(self, obj, remoteName, parentObject, repair):
    256          deleted = False
    257          try:
    258              ppath = obj.getPrimaryPath()
    259              getObjByPath(self, ppath)
    260          except (KeyError, NotFound):
    261              log.error("object %s in relation %s has been deleted " \
    262                           "from its primary path",
    263                           obj.getPrimaryId(), self.getPrimaryId())
    264              if repair:
    265                  log.warn("removing object %s from relation %s",
    266                           obj.getPrimaryId(), self.getPrimaryId())
    267                  self._objects.remove(obj)
    268                  self.__primary_parent__._p_changed = True
    269                  deleted = True
    270
    271          if not deleted:
    272              rrel = getattr(obj, remoteName)
    273              if not rrel.hasobject(parentObject):
    274                  log.error("remote relation %s doesn't point back to %s",
    275                                  rrel.getPrimaryId(), self.getPrimaryId())
    276                  if repair:
    277                      log.warn("reconnecting relation %s to relation %s",
    278                              rrel.getPrimaryId(),self.getPrimaryId())
    279                      rrel._add(parentObject)
    280          return deleted
    281
    282
    283 -    def checkRelation(self, repair=False):
    284          """Check to make sure that relationship bidirectionality is ok.
    285          """
    286          if len(self._objects):
    287              log.debug("checking relation: %s", self.id)
    288
    289          # look for objects that don't point back to us
    290          # or who should no longer exist in the database
    291          rname = self.remoteName()
    292          parobj = self.getPrimaryParent()
    293          for obj in self._objects:
    294              self.checkObjectRelation(obj, rname, parobj, repair)
    295
    296          # find duplicate objects
    297          keycount = {}
    298          for obj in self._objects:
    299              key = obj.getPrimaryId()
    300              c = keycount.setdefault(key, 0)
    301              c += 1
    302              keycount[key] = c
    303          # Remove duplicate objects or objects that don't exist
    304          for key, val in keycount.items():
    305              if val > 1:
    306                  log.critical("obj:%s rel:%s dup found obj:%s count:%s",
    307                               self.getPrimaryId(), self.id, key, val)
    308                  if repair:
    309                      log.critical("repair key %s", key)
    310                      self._objects = [ o for o in self._objects \
    311                                          if o.getPrimaryId() != key ]
    312                      try:
    313                          obj = self.getObjByPath(key)
    314                          self._objects.append(obj)
    315                      except KeyError:
    316                          log.critical("obj %s not found in database", key)
    317
    318
    319  InitializeClass(ToManyRelationship)
    320