diff --git a/relation/README.txt b/relation/README.txt index b7b1008..fc36e44 100644 --- a/relation/README.txt +++ b/relation/README.txt @@ -21,10 +21,62 @@ You are now ready to retrieve relations by using the relations registry's You may also like to read the file concepts.txt that gives you some more basic ideas about the relation package. - -Relations Management: create - register - query -=============================================== + +A Basic API for Relation Management +=================================== + +In object-oriented programming you usually don't care explicitly about +relations: you just assign an object to an attribute of another object +and you have created a relation beween these two objects. + +An example: Let's have two classes, Person and City, and we want to store the +fact that a person lives in a certain city. So if ``clark`` is an instance +of Person and ``washington`` an instance of City we can just say: +``clark.city = washington``. + +This works fine (even when you are dealing with persistent objects in Zope) +and is the standard way of establishing a relation in an object-oriented +programming language. + +But there are scenarios where this is not sufficient and you have to +care explicitly about relations. + +One would be the requirement to get all inhabitants of Washington: You could +of course do this by collecting all persons and check which ones have +set it's ``city`` attribute to ``washington``. This approach poses (at least) +two problems: + +- how can I find all instances of the Person class? + +- depending on the numbers of persons in my system checking all might take a + tremendous long time. + +You can easiliy resolve these problems by providing a corresponding attribute +on the City class, something like ``washington.inhabitants = [clark]`` and add +a person to this list every time you assign ``washington`` to a person's +``city`` attribute. + +Of course there are other things to consider, e.g. how to handle deletions +of objects. + +But for this simple kind of relationships just connecting two objects you +could in fact solve all this problems without the need for a special +relation management framework. Nevertheless, as this is a common pattern, +it would be helpful to have an ageed-upon standard how to handle such +cases; this might deal with the automatic housekeeping of redundant +assignments as well as with the deletion problem. + +Such a standard - and a corresponding relation management framework - is +getting really important when we deal with more complex use cases - involving +e.g. triadic relations (connecting three objects) like in +"kirk is the child of audrey and clark" or if we want the relation to carry +additional information, e.g. like in +"clark lived in washington from 1999 to 2003". + + +Relation Management at Work: create - register - query +====================================================== >>> from zope.app.testing.placelesssetup import setUp >>> setUp() diff --git a/relation/__init__.py b/relation/__init__.py index 5ac8c4b..4f5e156 100644 --- a/relation/__init__.py +++ b/relation/__init__.py @@ -38,6 +38,9 @@ class Relation(Persistent): def getPredicateName(cls): return '%s.%s' % (cls.__module__, cls.__name__) + def validate(self, registry=None): + return True + class DyadicRelation(Relation): diff --git a/relation/interfaces.py b/relation/interfaces.py index 0759d1d..53a9d51 100644 --- a/relation/interfaces.py +++ b/relation/interfaces.py @@ -28,17 +28,6 @@ from zope.app.event.interfaces import IObjectEvent # relation interfaces -class IPredicate(Interface): - """ A predicate signifies a relationship. This may be implemented - directly as a relation class, or the relation object may - hold the predicate as an attribute. - """ - - def getPredicateName(): - """ Return this predicate as a string that may be used for indexing. - """ - - class IRelation(Interface): """ Base interface for relations. """ @@ -47,6 +36,24 @@ class IRelation(Interface): """ Return the predicate of this relation as a string that may be used for indexing. """ + + def validate(registry=None): + """ Return True if this relation is valid. + + If the registry argument is provided the check should be done + with respect to this relation registry, e.g. to + """ + + +class IMonadicRelation(IRelation): + """ Relation with just one object. + + While a monadic relation could be easily represented by an attribute + or an annotation, monadic relations are e.g. useful when working with + an ontology-driven higher-level relation framework. + """ + + first = Attribute('First and only object that belongs to the relation.') class IDyadicRelation(IRelation): @@ -64,6 +71,17 @@ class ITriadicRelation(IDyadicRelation): third = Attribute('Third object that belongs to the relation.') +class IPredicate(Interface): + """ A predicate signifies a relationship. This may be implemented + directly as a relation class, or the relation object may + hold the predicate as an attribute. + """ + + def getPredicateName(): + """ Return this predicate as a string that may be used for indexing. + """ + + # event interfaces class IRelationInvalidatedEvent(IObjectEvent): @@ -92,16 +110,22 @@ class IRelationsRegistryQuery(Interface): """ Interface for querying a relations registry. """ - def query(**kw): + def query(relation=None, **kw): """ Return a sequence of relations that fulfill the criteria given. - Example: rr.queryRelations(first=someObject, second=anotherObject, - relationship=SomeRelationClass) + You may provide a relation object as an example that specifies the + search criteria, i.e. its predicate and first, second or third + attribute will be used for searching, or explicit criteria + via keyword arguments. + + Example for using keyword criteria: + rr.queryRelations(first=someObject, second=anotherObject, + relationship=SomeRelationClass) """ class IRelationsRegistry(IRelationsRegistryUpdate, IRelationsRegistryQuery): - """ Local utility for registering (cataloguing) and searching relations. + """ A registry for registering and searching relations typically + implemented as a local utility . """ - diff --git a/relation/registry.py b/relation/registry.py index ec36469..b4c3a61 100644 --- a/relation/registry.py +++ b/relation/registry.py @@ -54,7 +54,7 @@ class DummyRelationsRegistry(object): if relation in self.relations: self.relations.remove(relation) - def query(self, **kw): + def query(self, example=None, **kw): result = [] for r in self.relations: hit = True @@ -89,7 +89,7 @@ class RelationsRegistry(Catalog): def unregister(self, relation): self.unindex_doc(zapi.getUtility(IIntIds).getId(relation)) - def query(self, **kw): + def query(self, example=None, **kw): for k in kw: if k == 'relationship': quString = kw[k].getPredicateName()