[Zope-dev] RFC: RelationAware class for relations between objects

Roché Compaan roche@upfrontsystems.co.za
Fri, 2 May 2003 17:48:13 +0200


On Fri, 02 May 2003 09:47:37 -0400
Shane Hathaway <shane@zope.com> wrote:

> Roché Compaan wrote:
> > On Thu, 1 May 2003 23:30:04 +0200
> > Roché Compaan <roche@upfrontsystems.co.za> wrote:
> > 
> > 
> >>Something like ComputedAttribute or descriptors should make it possible.
> >>Hmm, I might just have thought of a way to do this with
> >>ComputedAttribute which I'll try tomorrow. But ComputedAttribute is
> >>Zope2 specific isn't it? Darn ...
> > 
> > 
> > It looks like ComputedAttribute has no dependencies on Zope 2 code so I
> > had a go at another API addressing specifically the way in which objects
> > use relationships.
> 
> The work you did looks good, but at this point I'd still like to call 
> the work you did a proof of concept.

Absolutely proof of concept. I wrote it with far to few hours of
sleep mainly because I lay awake thinking about it ;-)

> The next step is to write a proposal that others can comment on.  A
> long discussion like this is hard for others to follow, but a proposal
> sums up everything we learned.
> 
> > I used the Relations class in mxmRelations as is since it has no Zope
> > dependencies. How this will be made accessible as a different branch in
> > the ZODB (iow where it will be stored and how you will locate it) and
> > how the Relations API must be extended still needs to be addressed.
> 
> One request: the word "relation" should not appear anywhere in the API. 
>   We should use "relationship" consistently.  I'm pretty sure that 
> relations are only one possible implementation of relationship storage.

If you are that sure then the distinction is not that clear to me. I
understood "relation" as the *definition* of a logical association
between two objects and relationship as the association itself. Marriage
is the relation, Peter's marriage to Susan is the relationship. Sure,
there is some overlap with "relation" in relational theory but we
describe the same thing: a logical association. When you use the word
"relation" you are referring to it as a specific storage implementation:
a table.

But to stop the confusion we can talk about Relationship and
RelationshipStorage or just use the collective Relationships for the
storage location.

> 
> > I assume all objects have ids. This is not a requirement and if we drop
> > this assumption very little in the implementation have to change.
> 
> We can't make the assumption that all objects have IDs.  One of the 
> requirements is that either direct or indirect references are possible. 
>   All of the requirements should be listed on the proposal.

Just to be clear can you give me an example of a direct and indirect
references.

> > I do not define Relationships as static attributes since we need a hand=
le on
> > the class instance to relate.
> 
> That's what computed attributes and descriptors are for.
> 
> > from Relations import Relations # This is the Relations class in mxmRel=
ations
> > import ExtensionClass
> > from ComputedAttribute import ComputedAttribute
> > 
> > class Relationship(ExtensionClass.Base):
> > 
> >     def __init__(self, ob, relation, cardinality):
> >         self.ob = ob
> >         self.relation = relation
> >         self.cardinality = cardinality
> > 
> >     _r_ = ComputedAttribute(lambda self: self.relation._get(self.ob))
> 
> Actually, I meant for this class to be a ComputedAttribute/descriptor. 
> This class does not need to use computed attributes.

Iow, Relationship should subclass ComputedAttribute? I tried something
like that but ran into some problems and just wanted to get something
out there for comment.

> > 
> >     def __getattr__(self, name):
> >         l = self._r_
> >         if self.cardinality == 'single':
> >             return getattr(l[0], name)
> >         else:
> >             # This can optimised: Relations can return a BTree that we
> >             # can subscript
> >             for ob in l:
> >                 if ob.id == name:
> >                     return ob
> > 
> >     def add(self, other):
> >         # TODO: test if 'other' is not a sequence if our cardinality is
> >         # single
> >         self.relation._relate(self.ob, other)
> > 
> >     def remove(self, other):
> >         self.relation._unrelate(self.ob, other)
> > 
> > 
> > student_courses = Relations()
> > term_courses = Relations()
> > # this relation will be between an object that knows it relationships a=
nd 
> > # instances of third party objects that doesn't
> > school_courses = Relations()
> 
> Note that you did not arrange for the relations to be stored in ZODB at 
> all.  I'll help you deal with that once we've prepared an API.
> 
> If you have some time, I'd appreciate it if you started a proposal on a 
> wiki page.  Then we'll come up with an API.  Once we're satisfied with 
> the API, we'll ask for comments.

I'll ask Jean to help me with this - he's formulates much better than I
do and has a much better grip on the subtleties of language.

-- 
Roché Compaan
Upfront Systems                 http://www.upfrontsystems.co.za