Benutzung des ++attribute++-Namespaces in Zope3

In Zope3 kann man Objekte auf Attributen anderer Objekte speichern, dabei gibt es aber einiges zu beachten.

by Michael Howitz posted at 2006-02-06 14:26 last modified 2006-02-06 14:26

Wenn man in Zope3 ein Objekt child in ein Container-Objekt parent legt kann man dieses in der URL über parent/child
ansprechen. Will man aber ein Objekt auf einem Attribut eines anderen Objektes speichern geht man folgendermaßen vor:

Einfachster Fall

interfaces.py

from zope.interface import Interface, Attribute

class IChild(Interface):
"Das Kind."

class IParent(Interface):
"Das Elter."
child = Attribute("Kind-Objekt")

impl.py

from persistent import Persistent
from zope.interface import implements
from zope.app.container.contained import Contained

from myproj.interfaces import IChild, IParent

class Child(Persistent, Contained):
"""Kindklasse."""
implements(IChild)

class Parent(Persistent, Contained):
"""Elternklasse, die Kind auf Attrbitut tragen soll."""
implements(IParent)

def __init__(self, *args, **kw):
super (Parent, self).__init__(*args, **kw)
child = Child()
self.child = child
child.__parent__ = self
child.__name__ = '++attribute++child'

Wenn man von child zu parent traversieren können will, müssen __parent__ und __name__ gesetzt sein. In __parent__ steht das Elter in der Hierachie (also self). In __name__ steht, wie von parent zu child traversiert werden kann. Der Präfix '++attribute++' vor dem Attributnamen ist wichtig, damit der Traversierer weiß, wo er nach dem attribut suchen soll. (Das ist besonders wichtig, wenn parent ein Container ist, da dort eine Traversierung ohne Angabe eines Namespace-Präfix immer in den Container hineintraversiert und damit child nicht finden kann.)

Benutzung von UIDs

Wenn man für child eine UID benötigt, muss man child selbst beim UID-Utility registrieren und beim Löschen von parent selbst wieder dafür sorgen, dass die UID für child gelöscht wird (das wird sonst beim Hinzufügen und Löschen im Container automatisch über Events erledigt). Das Zope3-UID-Utility erwartet, dass ein Objekt für welches es eine UID generieren soll in einem Kontext eingebunden ist, der bis zur Wurzel zurückverfolgt werden kann. Also scheidet die Variante child im __init__ von parent anzulegen aus, da parent zu diesem Zeitpunkt noch keinen Kontext hat. (Es würde eine NotYet-Exception auftreten.)

An interfaces.py ändert sich nichts. impl.py ändert sich wie folgt:

from persistent import Persistent
from zope.app.container.contained import Contained

class Child(Persistent, Contained):
"""Kindklasse."""

class Parent(Persistent, Contained):
"""Elternklasse, die Kind auf Attrbitut tragen soll."""

child = None

In der configure.zcml wird für die Events IObjectAddedEvent und IObjectRemovedEvent ein Subscriber eingetragen:

<subscriber
for="zope.app.container.interfaces.IObjectAddedEvent"
handler=".eventhandler.onObjectAdded" />

<subscriber
for="zope.app.container.interfaces.IObjectRemovedEvent"
handler=".eventhandler.onObjectRemoved" />

In eventhandler.py benötigt man dann folgende zwei Funktionen:

import zope.event
from zope.app.intid.interfaces import IIntIds
from zope.app.container.contained import ObjectRemovedEvent
from myproj.interfaces import IParent
from mypro.impl import Child

def onObjectAdded(event):
obj = event.object

if IParent.providedBy(obj):
child = Child()
obj.child = child
child.__parent__ = obj
child.__name__ = u"++attribute++child"
uid_util = zapi.getUtility(IIntIds, context=obj)
uid_util.register(child)

def onObjectRemoved(event):
obj = event.object
if IParent.providedBy(obj):
zope.event.notify(ObjectRemovedEvent(obj.child, obj, obj.child.__name__))

onObjectAdded sorgt für die Initialisierung und Registrierung von child am UID-Utility.
onObjectRemoved teilt per Event mit, dass mit parent auch child gelöscht wurde. Das UID-Utility lauscht an diesem Event und löscht entsprechend die UID für child.

Category(s)
Zope 3
The URL to Trackback this entry is:
http://blog.gocept.com/benutzung-des-attribute-namespaces-in-zope3/tbping