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): attributes are also needed later when we send an IObjectRemovedEvent event):
>>> from persistent import Persistent >>> from persistent import Persistent
>>> from zope.interface import implements
>>> from cybertools.relation.interfaces import IRelatable
>>> class Person(Persistent): >>> class Person(Persistent):
... __name__ = __parent__ = None ... __name__ = __parent__ = None
... implements(IRelatable)
>>> class City(Persistent): >>> class City(Persistent):
... pass ... implements(IRelatable)
>>> clark = Person() >>> clark = Person()
>>> kirk = Person() >>> kirk = Person()
@ -138,6 +142,12 @@ relations, first and second:
>>> len(nyRels) >>> len(nyRels)
2 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: It is also possible to remove a relation from the relation registry:
>>> relations.unregister( >>> relations.unregister(
@ -254,6 +264,12 @@ if we want to access relations by array index:
>>> len(nyRels) >>> len(nyRels)
2 2
We can also query the registry using an example relation:
>>> clarkRels = relations.query(example=LivesIn(clark, None))
>>> len(clarkRels)
1
>>> relations.unregister( >>> relations.unregister(
... list(relations.query(first=audrey, second=newyork))[0] ... list(relations.query(first=audrey, second=newyork))[0]
... ) ... )
@ -355,10 +371,12 @@ creating a special relation class that uses named predicates.
>>> class PredicateRelation(DyadicRelation): >>> class PredicateRelation(DyadicRelation):
... def __init__(self, predicate, first, second): ... def __init__(self, predicate, first, second):
... self.predicate = predicate ... self.predicate = predicate
... predicate.forClass = self.__class__
... self.first = first ... self.first = first
... self.second = second ... self.second = second
... def getPredicateName(self): ... 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 We also need a class for the predicate objects that will be used for
the constructor of the NamedPredicateRelation class: the constructor of the NamedPredicateRelation class:
@ -370,7 +388,10 @@ the constructor of the NamedPredicateRelation class:
... implements(IPredicate) ... implements(IPredicate)
... def __init__(self, name): ... def __init__(self, name):
... self.name = name ... self.name = name
... self.forClass = None
... def getPredicateName(self): ... def getPredicateName(self):
... if self.forClass is not None:
... return self.forClass(self, None, None).getPredicateName()
... return self.name ... return self.name
We can now create a predicate with the name '_lives in_' (that may replace 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 zope.interface import implements
from interfaces import IPredicate from interfaces import IPredicate
from interfaces import IRelation, IDyadicRelation, ITriadicRelation from interfaces import IRelation, IDyadicRelation, ITriadicRelation
from interfaces import IRelatable
class Relation(Persistent): class Relation(Persistent):
@ -41,6 +42,12 @@ class Relation(Persistent):
def validate(self, registry=None): def validate(self, registry=None):
return True 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): class DyadicRelation(Relation):
@ -49,6 +56,7 @@ class DyadicRelation(Relation):
def __init__(self, first, second): def __init__(self, first, second):
self.first = first self.first = first
self.second = second self.second = second
self.checkRelatable(first, second)
class TriadicRelation(Relation): class TriadicRelation(Relation):
@ -59,4 +67,5 @@ class TriadicRelation(Relation):
self.first = first self.first = first
self.second = second self.second = second
self.third = third 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.') 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): class IPredicate(Interface):
""" A predicate signifies a relationship. This may be implemented """ A predicate signifies a relationship. This may be implemented
directly as a relation class, or the relation object may 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 # relation registry interfaces
class IRelationRegistryUpdate(Interface): class IRelationRegistryUpdate(Interface):
@ -105,15 +131,12 @@ class IRelationRegistryUpdate(Interface):
""" Remove the relation given from this registry. """ Remove the relation given from this registry.
""" """
#BBB
#IRelationsRegistryUpdate = IRelationRegistryUpdate
class IRelationRegistryQuery(Interface): class IRelationRegistryQuery(Interface):
""" Interface for querying a relation registry. """ 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. """ Return a sequence of relations that fulfill the criteria given.
You may provide a relation object as an example that specifies the 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, rr.queryRelations(first=someObject, second=anotherObject,
relationship=SomeRelationClass) relationship=SomeRelationClass)
""" """
#BBB
#IRelationsRegistryQuery = IRelationRegistryQuery
class IRelationRegistry(IRelationRegistryUpdate, IRelationRegistryQuery): class IRelationRegistry(IRelationRegistryUpdate, IRelationRegistryQuery):
@ -134,6 +155,4 @@ class IRelationRegistry(IRelationRegistryUpdate, IRelationRegistryQuery):
implemented as a local utility . implemented as a local utility .
""" """
#BBB
#IRelationsRegistry = IRelationRegistry

View file

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