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:
parent
c04f84ed8f
commit
3c8b67a378
4 changed files with 105 additions and 19 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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).
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue