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:
parent
84dd0ce76a
commit
a5cb31fa47
4 changed files with 92 additions and 23 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue