more on relations - now includes an IObjectRemoved event handler and more fine-grained permissions

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@657 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2005-11-05 14:13:47 +00:00
parent c04f84ed8f
commit 3c8b67a378
4 changed files with 105 additions and 19 deletions

View file

@ -1,13 +1,19 @@
Yet Another Relations Engine... Yet Another Relations (Reference) Engine...
=============================== ===========================================
>>> from zope.app.testing.placelesssetup import setUp
>>> setUp()
Let's start with two classes and a few objects that we will connect using Let's start with two classes and a few objects that we will connect using
relations: relations (we derive the classes from Persistent, thus we will be able to
reference these objects via IntIds later; the __parent__ and __name__
attributes are also needed later when we send an IObjectRemovedEvent event):
>>> class Person(object): >>> from persistent import Persistent
... pass >>> class Person(Persistent):
... __name__= __parent__ = None
>>> class City(object): >>> class City(Persistent):
... pass ... pass
>>> clark = Person() >>> clark = Person()
@ -20,6 +26,7 @@ The relation we'll use tells us in which city a person lives; this is a dyadic
relation as it connects two objects. We also associate the relationship relation as it connects two objects. We also associate the relationship
with an interface as we will later use this interface for querying relations. with an interface as we will later use this interface for querying relations.
Dyadic Relations Dyadic Relations
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -65,6 +72,7 @@ It is also possible to remove a relation from the relation registry:
>>> nyRels[0].first == kirk >>> nyRels[0].first == kirk
True True
Triadic Relations Triadic Relations
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -97,14 +105,19 @@ all relations for clark's children:
>>> clarkChildren[0].third == kirk >>> clarkChildren[0].third == kirk
True True
Setting up and using a RelationsRegistry local utility Setting up and using a RelationsRegistry local utility
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We now do the same stuff as above with a real, catalog-based implementation of We now do the same stuff as above with a real, catalog-based implementation of
the relations registry. the relations registry. We also register the relations registry as a
utility for demonstration purposes (and to be able to use it later when
working with events).
>>> from cybertools.relation.registry import RelationsRegistry >>> from cybertools.relation.registry import RelationsRegistry
>>> relations = RelationsRegistry() >>> from cybertools.relation.interfaces import IRelationsRegistry
>>> from zope.app.testing import ztapi
>>> ztapi.provideUtility(IRelationsRegistry, RelationsRegistry())
In order to register relations the objects that are referenced have to be In order to register relations the objects that are referenced have to be
registered with an IntIds (unique ids) utility, so we have to set up such registered with an IntIds (unique ids) utility, so we have to set up such
@ -112,7 +125,6 @@ an utility (using a stub/dummy implementation for testing purposes):
>>> from cybertools.relation.tests import IntIdsStub >>> from cybertools.relation.tests import IntIdsStub
>>> from zope.app.intid.interfaces import IIntIds >>> from zope.app.intid.interfaces import IIntIds
>>> from zope.app.testing import ztapi
>>> ztapi.provideUtility(IIntIds, IntIdsStub()) >>> ztapi.provideUtility(IIntIds, IntIdsStub())
We also have to provide an adapter for the Relation objects that provides We also have to provide an adapter for the Relation objects that provides
@ -127,6 +139,9 @@ the attributes needed for indexing:
So we are ready again to register a set of relations with our new relations So we are ready again to register a set of relations with our new relations
registry and query it. registry and query it.
>>> from zope.app import zapi
>>> relations = zapi.getUtility(IRelationsRegistry)
>>> relations.register(LivesIn(clark, washington)) >>> relations.register(LivesIn(clark, washington))
>>> relations.register(LivesIn(audrey, newyork)) >>> relations.register(LivesIn(audrey, newyork))
>>> relations.register(LivesIn(kirk, newyork)) >>> relations.register(LivesIn(kirk, newyork))
@ -170,3 +185,32 @@ It should work also for triadic relations:
>>> clarkChildren[0].third == kirk >>> clarkChildren[0].third == kirk
True True
Handling object removal
~~~~~~~~~~~~~~~~~~~~~~~
Often it is desirable to unregister a when one of the objects
involved in the relation is removed from its container. This can be
done by subscribing to IObjectRemovedEvent. The relation.registry module
provides a simple handler for this event.
>>> from zope.app.container.interfaces import IObjectRemovedEvent
>>> from zope.interface import Interface
>>> from cybertools.relation.registry import unregisterRelations
>>> ztapi.subscribe([Interface, IObjectRemovedEvent], None, unregisterRelations)
We simulate the removal of kirk by calling notify:
>>> from zope.app.container.contained import ObjectRemovedEvent
>>> from zope.event import notify
>>> len(relations.query(first=clark))
2
>>> notify(ObjectRemovedEvent(kirk))
Thus there should only remain one relation containing clark as first:
>>> len(relations.query(first=clark))
1

View file

@ -7,12 +7,16 @@
<localUtility class=".registry.RelationsRegistry"> <localUtility class=".registry.RelationsRegistry">
<require <require
permission="zope.Public" permission="zope.View"
interface=".interfaces.IRelationsRegistry" interface=".interfaces.IRelationsRegistryQuery"
/>
<require
permission="zope.ManageContent"
interface=".interfaces.IRelationsRegistryUpdate"
/> />
<require <require
interface="zope.app.catalog.interfaces.ICatalogQuery" interface="zope.app.catalog.interfaces.ICatalogQuery"
permission="zope.Public" permission="zope.View"
/> />
<require <require
interface="zope.app.catalog.interfaces.ICatalogEdit" interface="zope.app.catalog.interfaces.ICatalogEdit"
@ -29,8 +33,17 @@
for=".interfaces.IRelation" for=".interfaces.IRelation"
provides=".registry.IIndexableRelation" provides=".registry.IIndexableRelation"
factory=".registry.IndexableRelationAdapter" factory=".registry.IndexableRelationAdapter"
trusted="true"
/> />
<subscriber
for="zope.interface.Interface
zope.app.container.interfaces.IObjectRemovedEvent"
handler=".registry.unregisterRelations"
/>
<!-- browser settings -->
<browser:tool <browser:tool
interface=".interfaces.IRelationsRegistry" interface=".interfaces.IRelationsRegistry"
title="Relations Registry" title="Relations Registry"

View file

@ -25,8 +25,9 @@ $Id$
from zope.interface import Interface, Attribute from zope.interface import Interface, Attribute
class IRelationsRegistry(Interface): class IRelationsRegistryUpdate(Interface):
""" Local utility for registering (cataloguing) and searching relations. """ Interface for registering and unregistering relations with a
relations registry.
""" """
def register(relation): def register(relation):
@ -37,6 +38,11 @@ class IRelationsRegistry(Interface):
""" Remove the relation given from this registry. """ Remove the relation given from this registry.
""" """
class IRelationsRegistryQuery(Interface):
""" Interface for querying a relations registry.
"""
def query(**kw): def query(**kw):
""" Return a list of relations that fulfill the criteria given. """ Return a list of relations that fulfill the criteria given.
@ -45,14 +51,15 @@ class IRelationsRegistry(Interface):
""" """
class IRelationsRegistry(IRelationsRegistryUpdate, IRelationsRegistryQuery):
""" Local utility for registering (cataloguing) and searching relations.
"""
class IRelation(Interface): class IRelation(Interface):
""" Base class for relations. """ Base class for relations.
""" """
relationship = Attribute(
'A string specific for a kind of relation; e.g. the full module '
'path and class name of the class implementing a certain type of relation.')
class IDyadicRelation(IRelation): class IDyadicRelation(IRelation):
""" Relation connecting two objects. """ Relation connecting two objects.

View file

@ -22,6 +22,7 @@ Implementation of the utilities needed for the relations package.
$Id$ $Id$
""" """
from persistent import Persistent
from zope.interface import Interface, Attribute, implements from zope.interface import Interface, Attribute, implements
from zope.app import zapi from zope.app import zapi
from zope.app.catalog.catalog import Catalog from zope.app.catalog.catalog import Catalog
@ -120,8 +121,13 @@ class IndexableRelationAdapter(object):
def __getattr__(self, attr): def __getattr__(self, attr):
value = getattr(self.context, attr) value = getattr(self.context, attr)
return _getUid(value) if isinstance(value, Persistent):
return _getUid(value)
else:
return value
# helper functions
def _getUid(ob): def _getUid(ob):
return zapi.getUtility(IIntIds).getId(ob) return zapi.getUtility(IIntIds).getId(ob)
@ -132,3 +138,19 @@ def _getRelationship(relation):
def _getClassString(cls): def _getClassString(cls):
return cls.__module__ + '.' + cls.__name__ return cls.__module__ + '.' + cls.__name__
# event handler
def unregisterRelations(context, event):
""" Handles IObjectRemoved event: unregisters all relations for the
object that has been removed.
"""
relations = []
registry = zapi.getUtility(IRelationsRegistry)
for attr in ('first', 'second', 'third'):
relations = registry.query(**{attr: context})
for relation in relations:
registry.unregister(relation)
# to do: unregister relation also from the IntId utility
# (if appropriate).