Some improvements on relation registry stuff

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1089 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-02-20 08:03:01 +00:00
parent 84dd0ce76a
commit a5cb31fa47
4 changed files with 92 additions and 23 deletions

View file

@ -87,11 +87,15 @@ reference these objects via IntIds later; the __parent__ and __name__
attributes are also needed later when we send an IObjectRemovedEvent event):
>>> from persistent import Persistent
>>> from zope.interface import implements
>>> from cybertools.relation.interfaces import IRelatable
>>> class Person(Persistent):
... __name__ = __parent__ = None
... implements(IRelatable)
>>> class City(Persistent):
... pass
... implements(IRelatable)
>>> clark = Person()
>>> kirk = Person()
@ -138,6 +142,12 @@ relations, first and second:
>>> len(nyRels)
2
We can also query the registry using an example relation:
>>> clarkRels = relations.query(example=LivesIn(clark, None))
>>> len(clarkRels)
1
It is also possible to remove a relation from the relation registry:
>>> relations.unregister(
@ -254,6 +264,12 @@ if we want to access relations by array index:
>>> len(nyRels)
2
We can also query the registry using an example relation:
>>> clarkRels = relations.query(example=LivesIn(clark, None))
>>> len(clarkRels)
1
>>> relations.unregister(
... list(relations.query(first=audrey, second=newyork))[0]
... )
@ -355,10 +371,12 @@ creating a special relation class that uses named predicates.
>>> class PredicateRelation(DyadicRelation):
... def __init__(self, predicate, first, second):
... self.predicate = predicate
... predicate.forClass = self.__class__
... self.first = first
... self.second = second
... def getPredicateName(self):
... return self.predicate.getPredicateName()
... baseName = super(PredicateRelation, self).getPredicateName()
... return '.'.join((baseName, self.predicate.name))
We also need a class for the predicate objects that will be used for
the constructor of the NamedPredicateRelation class:
@ -370,7 +388,10 @@ the constructor of the NamedPredicateRelation class:
... implements(IPredicate)
... def __init__(self, name):
... self.name = name
... self.forClass = None
... def getPredicateName(self):
... if self.forClass is not None:
... return self.forClass(self, None, None).getPredicateName()
... return self.name
We can now create a predicate with the name '_lives in_' (that may replace

View file

@ -29,6 +29,7 @@ from persistent import Persistent
from zope.interface import implements
from interfaces import IPredicate
from interfaces import IRelation, IDyadicRelation, ITriadicRelation
from interfaces import IRelatable
class Relation(Persistent):
@ -40,6 +41,12 @@ class Relation(Persistent):
def validate(self, registry=None):
return True
def checkRelatable(self, *objects):
for obj in objects:
if obj is not None and not IRelatable.providedBy(obj):
raise(ValueError, 'Objects to be used in relations '
'must provide the IRelatable interface.')
class DyadicRelation(Relation):
@ -49,6 +56,7 @@ class DyadicRelation(Relation):
def __init__(self, first, second):
self.first = first
self.second = second
self.checkRelatable(first, second)
class TriadicRelation(Relation):
@ -59,4 +67,5 @@ class TriadicRelation(Relation):
self.first = first
self.second = second
self.third = third
self.checkRelatable(first, second, third)

View file

@ -71,6 +71,23 @@ class ITriadicRelation(IDyadicRelation):
third = Attribute('Third object that belongs to the relation.')
# this is just a conceptual try - thinking about storing
# relations as attributes...
class IAttributeRelation(IDyadicRelation):
""" A type of relation that will be stored in attributes of the
objects that take part in the relation. You have to use a
relation registry that provides IAttributeRelationRegistry
for registering/managing relations of this type.
"""
attributeNameFirst = Attribute('Name of the attribute in which the '
'relation will be stored on the `first` object. '
'Typically a class attribute.')
attributeNameSecond = Attribute('Name of the attribute in which the '
'relation will be stored on the `second` object.'
'Typically a class attribute.')
class IPredicate(Interface):
""" A predicate signifies a relationship. This may be implemented
directly as a relation class, or the relation object may
@ -90,6 +107,15 @@ class IRelationInvalidatedEvent(IObjectEvent):
"""
# marker interfaces
class IRelatable(Interface):
""" Marker interface for objects that may have relations associated
with them. Should be checked by IRelationRegistry.register()
and event handlers.
"""
# relation registry interfaces
class IRelationRegistryUpdate(Interface):
@ -105,15 +131,12 @@ class IRelationRegistryUpdate(Interface):
""" Remove the relation given from this registry.
"""
#BBB
#IRelationsRegistryUpdate = IRelationRegistryUpdate
class IRelationRegistryQuery(Interface):
""" Interface for querying a relation registry.
"""
def query(relation=None, **kw):
def query(example=None, **kw):
""" Return a sequence of relations that fulfill the criteria given.
You may provide a relation object as an example that specifies the
@ -125,8 +148,6 @@ class IRelationRegistryQuery(Interface):
rr.queryRelations(first=someObject, second=anotherObject,
relationship=SomeRelationClass)
"""
#BBB
#IRelationsRegistryQuery = IRelationRegistryQuery
class IRelationRegistry(IRelationRegistryUpdate, IRelationRegistryQuery):
@ -134,6 +155,4 @@ class IRelationRegistry(IRelationRegistryUpdate, IRelationRegistryQuery):
implemented as a local utility .
"""
#BBB
#IRelationsRegistry = IRelationRegistry

View file

@ -57,23 +57,27 @@ class DummyRelationRegistry(object):
def query(self, example=None, **kw):
result = []
criteria = {}
if example is not None:
for attr in ('first', 'second', 'third',):
value = getattr(example, attr, None)
if value is not None:
criteria[attr] = value
criteria['relationship'] = example
criteria.update(kw)
for r in self.relations:
hit = True
for k in kw:
#if ((k == 'relationship' and r.__class__ != kw[k])
for k in criteria:
if ((k == 'relationship'
and r.getPredicateName() != kw[k].getPredicateName())
and r.getPredicateName() != criteria[k].getPredicateName())
or (k != 'relationship'
and (not hasattr(r, k) or getattr(r, k) != kw[k]))):
and (not hasattr(r, k) or getattr(r, k) != criteria[k]))):
hit = False
break
if hit:
result.append(r)
return result
#BBB
#DummyRelationsRegistry = DummyRelationRegistry
class RelationRegistry(Catalog):
""" Local utility for registering (cataloguing) and searching relations.
@ -97,14 +101,26 @@ class RelationRegistry(Catalog):
notify(RelationInvalidatedEvent(relation))
def query(self, example=None, **kw):
intIds = zapi.getUtility(IIntIds)
criteria = {}
if example is not None:
for attr in ('first', 'second', 'third',):
value = getattr(example, attr, None)
if value is not None:
criteria[attr] = intIds.getId(value)
pn = example.getPredicateName()
if pn:
criteria['relationship'] = pn
for k in kw:
# overwrite example fields with explicit values
if k == 'relationship':
quString = kw[k].getPredicateName()
criteria[k] = kw[k].getPredicateName()
else:
quString = zapi.getUtility(IIntIds).getId(kw[k])
criteria[k] = intIds.getId(kw[k])
for k in criteria:
# set min, max
kw[k] = (quString, quString)
return self.searchResults(**kw)
criteria[k] = (criteria[k], criteria[k])
return self.searchResults(**criteria)
#BBB
#RelationsRegistry = RelationRegistry
@ -207,9 +223,13 @@ def invalidateRelations(context, event):
""" Handles IObjectRemoved event: unregisters
all relations the object to be removed is involved in.
"""
# TODO: check marker interface of object:
# if not IRelatable.providedBy(event.object):
# return
relations = []
registry = zapi.queryUtility(IRelationRegistry)
if registry is not None:
#registry = zapi.queryUtility(IRelationRegistry)
registries = zapi.getAllUtilitiesRegisteredFor(IRelationRegistry)
for registry in registries:
for attr in ('first', 'second', 'third'):
relations = registry.query(**{attr: context})
for relation in relations: