Developer's Guide

  • Docs Home
  • Community Home

3. Many-to-Many (M:N) Relationships

This is a real example from Device.py which illustrates a many-to-many relationship between many Devices and many Device Groups.

...
from Products.ZenRelations.RelSchema import *
...
class Device(ManagedEntity, Commandable):
...
event_key = portal_type = meta_type = 'Device'

default_catalog = "deviceSearch" #device ZCatalog

relationshipManagerPathRestriction = '/Devices'
...
_relations = ManagedEntity._relations + (
("groups", ToMany(ToMany, "DeviceGroup", "devices")),
)
...

From DeviceGroup.py

...
from Products.ZenRelations.RelSchema import *
...
class DeviceGroup(DeviceOrganizer):
...
# Organizer configuration
dmdRootName = "Groups"

portal_type = meta_type = event_key = 'DeviceGroup'

_relations = DeviceOrganizer._relations + (
("devices", ToMany(ToMany,"Device","groups")),
)
...

According to this relationship there can be more than one Device assigned to a Device Group and more than one Device Group assigned to a Device. This relationship is created by:

  • Importing ToMany from Products.ZenRelations.RelSchema.

  • Appending a two-item tuple to the _relations attribute

    • The first item in the tuple is a "string" object which is the local name

    • The second item in the tuple is a RelSchema object which represents the relationship to another class. In this case the ToMany constructor creates/returns the RelSchema object.

      The RelSchema constructors take three parameters

      • The first parameter is a "type" object, "remoteType" which represents the relationship from another class. The "type" should be of a class derived from RelSchema

      • The second parameter is a "string" object, "remoteClass" which is the class name of the relative. In this case it is again the ToMany relationship.

      • The third parameter is a "string" object, "remoteName" which the remote name of itself.

  • Appending a complementary two-item tuple to the _relations attribute in the relative class.

3.1. One-to-Many (1:N) Container Relationships

Device to Hard Drives

This is a real example which illustrates a one-to-many relationship between one DeviceHW and many HardDrives where a DeviceHW object contains HardDrives.

From DeviceHW.py

...
from Products.ZenRelations.RelSchema import *
...
class DeviceHW(Hardware):
...
meta_type = "DeviceHW"
...
_relations = Hardware._relations + (
("harddisks", ToManyCont(ToOne, "HardDisk", "hw")),
)
...

From HardDisk.py

...
from Products.ZenRelations.RelSchema import *
...
class HardDisk(HWComponent):
...
portal_type = meta_type = 'HardDisk'
...
_relations = HWComponent._relations + (
("hw", ToOne(ToManyCont, "DeviceHW", "harddisks")),
)
...

According to this relationship there can be only one DeviceHW assigned to a HardDisk but more than one HardDisk assigned to a DeviceHW. This relationship is created by:

  1. Importing ToOne and ToManyCont from Products.ZenRelations.RelSchema.

  2. Appending a two-item tuple of to the _relations attribute

    1. The first item in the tuple is a "string" object which is the local name

    2. The second item in the tuple is a RelSchema object which represents the relationship to another class.

    RelSchema constructors take three parameters

    1. The first parameter is a "type" object, "remoteType" which represents the relationship from another class. The "type" should be of a class derived from RelSchema

    2. The second parameter is a "string" object, "remoteClass" which is the class name of the relative.

    3. The third parameter is a "string" object, "remoteName" which the remote name of itself.

  3. Appending a complementary two-item tuple to the _relations attribute in the relative class.

Specifying the remoteClass in a Relationship

The remoteClass parameter can be specified in a relationship by two methods.

("admin", ToOne(ToOne, "Admin", "server"))

In the example above "Admin" is the remote class on the relationship. For this to work properly the module "Admin" must be in the python path and it must contain a class named "Admin".

This behavior can be modified by using the attribute zenRelationsBaseModule. For instance if Admin was located in the path Products.ZenModel you could set zenRelationsBase = "Products.ZenModel". Now the remote class is in the module Products.ZenModel.Admin and the class must be Named "Admin".

If you wish to put multiple classes into one module and use them in relations you can add the class name to the end of the remoteClass value. For instance "Admin.Test" would access the module Admin with the class Test.

If the two classes in a relation are in a different packages then you can use the fully qualified path to the class. For instance here are the definitions of two classes in different packages: Products.ZenWidgets.Menu and Products.ZenModel.DeviceOrganizer.

In Products.ZenWidget.Menu.py

...
class Menu(ZenModelRM):
...
    _relations = (
                  ("deviceOrg",
                  ToOne(ToManyCont,
                  "Products.ZenModel.DeviceOrganizer",
                  "menus")),
                  )
...

In Products.ZenModel.DeviceOrgaizer.py

...
class DeviceOrganizer(ZenModelRM):
...
    _relations = (
                  ("menus",
                   ToManyCont(ToOne,
                   "Products.ZenWidget.Menu",
                   "deviceOrg")),
                 )
...