~justas.sadzevicius/schooltool/content_provider_api

« back to all changes in this revision

Viewing changes to src/schooltool/relationship/README.txt

  • Committer: Gediminas Paulauskas
  • Date: 2011-03-23 15:35:36 UTC
  • mfrom: (2748.1.4 schooltool-docs)
  • Revision ID: menesis@pov.lt-20110323153536-s1sg4o6ixym0lb11
Minor docs updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
Relationship
2
2
============
3
3
 
4
 
``schooltool.relationship`` is a library for managing arbitrary many-to-many binary
5
 
relationships.
 
4
``schooltool.relationship`` is a library for managing arbitrary many-to-many
 
5
binary relationships.
6
6
 
7
7
 
8
8
Quick overview
10
10
 
11
11
This package lets you define arbitrary many-to-many relationship schemas and
12
12
use them to relate arbitrary components (that are annotatable or have an
13
 
adapter to IRelationshipLinks).
 
13
adapter to `IRelationshipLinks`).
14
14
 
15
15
Usage example::
16
16
 
69
69
    ...                       'Member', 'A group member role.')
70
70
 
71
71
To demonstrate relationships we need some objects that can be related.  Any
72
 
object that has an adapter to IRelationshipLinks can be used in relationships.
73
 
Since schooltool.relationship provides a default adapter from IAnnotatable
74
 
to IRelationshipLinks, it is enough to declare that our objects are
75
 
IAttributeAnnotatable.
 
72
object that has an adapter to `IRelationshipLinks` can be used in relationships.
 
73
Since schooltool.relationship provides a default adapter from `IAnnotatable`
 
74
to `IRelationshipLinks`, it is enough to declare that our objects are
 
75
`IAttributeAnnotatable`.
76
76
 
77
77
    >>> from zope.interface import implements
78
78
    >>> from zope.annotation.interfaces import IAttributeAnnotatable
89
89
    >>> setup.placelessSetUp()
90
90
    >>> setup.setUpAnnotations()
91
91
 
92
 
We need to define the adapter from IAnnotatable to IRelationshipLinks.
 
92
We need to define the adapter from `IAnnotatable` to `IRelationshipLinks`.
93
93
In real life you would include the ZCML configuration of the
94
 
'schooltool.relationship' package via Zope 3 package includes.  In a test
95
 
you can use setUpRelationships from schooltool.relationship.tests.
 
94
``schooltool.relationship`` package via Zope 3 package includes.  In a test
 
95
you can use `setUpRelationships` from ``schooltool.relationship.tests``.
96
96
 
97
97
    >>> from schooltool.relationship.tests import setUpRelationships
98
98
    >>> setUpRelationships()
106
106
 
107
107
Since you will always want to use a particular set of roles for a given
108
108
relationship type, you can define a relationship schema and use it as a
109
 
shortcut:
 
109
shortcut::
110
110
 
111
111
    >>> from schooltool.relationship import RelationshipSchema
112
112
    >>> Membership = RelationshipSchema(URIMembership, group=URIGroup,
116
116
    >>> Membership(member=lilfroggy, group=frogs)
117
117
 
118
118
If you try to create the same relationship between the same objects more
119
 
than once, you will get a DuplicateRelationship exception.
 
119
than once, you will get a `DuplicateRelationship` exception.
120
120
 
121
121
    >>> Membership(member=lilfroggy, group=frogs)
122
122
    Traceback (most recent call last):
132
132
    ...     items.sort()
133
133
    ...     return [row[-1] for row in items]
134
134
 
135
 
For example, you can get a list of all members of the `frogs` group like
 
135
For example, you can get a list of all members of the "frogs" group like
136
136
this:
137
137
 
138
138
    >>> from schooltool.relationship import getRelatedObjects
145
145
    >>> getRelatedObjects(frogger, URIGroup)
146
146
    [frogs]
147
147
 
148
 
You can also explicitly say that you want all URIGroups that participate
149
 
in a URIMembership relationship.
 
148
You can also explicitly say that you want all `URIGroup`'s that participate
 
149
in a `URIMembership` relationship.
150
150
 
151
151
    >>> getRelatedObjects(frogger, URIGroup, URIMembership)
152
152
    [frogs]
155
155
 
156
156
In general, avoid reusing the same role for different relationship types.
157
157
 
158
 
You can remove relationships by calling the `unrelate` function
 
158
You can remove relationships by calling the `unrelate` function::
159
159
 
160
160
    >>> from schooltool.relationship import unrelate
161
161
    >>> unrelate(URIMembership, (frogs, URIGroup), (frogger, URIMember))
189
189
-----------------------
190
190
 
191
191
You can define a property in a class and use it instead of explicitly
192
 
calling global functions and passing roles around.
 
192
calling global functions and passing roles around::
193
193
 
194
194
    >>> from schooltool.relationship import RelationshipProperty
195
195
 
199
199
    >>> class Member(SomeObject):
200
200
    ...     groups = RelationshipProperty(URIMembership, URIMember, URIGroup)
201
201
 
202
 
Usage example:
 
202
Usage example::
203
203
 
204
204
    >>> fido = Member('fido')
205
205
    >>> dogs = Group('dogs')
239
239
    >>> old_subscribers = zope.event.subscribers
240
240
    >>> zope.event.subscribers = []
241
241
 
242
 
Before you establish a relationship, a BeforeRelationshipEvent is sent out.
243
 
You can implement constraints by raising an exception in an event subscriber.
 
242
Before you establish a relationship, a `BeforeRelationshipEvent` is sent out.
 
243
You can implement constraints by raising an exception in an event subscriber::
244
244
 
245
245
    >>> from zope.interface import Interface, directlyProvides
246
246
    >>> class IFrog(Interface):
261
261
      ...
262
262
    Exception: Only frogs can be members of the frogs group
263
263
 
264
 
When you establish a relationship, a RelationshipAddedEvent is sent out.
 
264
When you establish a relationship, a `RelationshipAddedEvent` is sent out::
265
265
 
266
266
    >>> from schooltool.relationship.interfaces import IRelationshipAddedEvent
267
267
    >>> def my_subscriber(event):
279
279
    >>> Membership(member=kermit, group=frogs)
280
280
    Relationship Membership added between kermit (Member) and frogs (Group)
281
281
 
282
 
Before you break a relationship, a BeforeRemovingRelationshipEvent is sent out.
283
 
You can implement constraints by raising exceptions in the subscriber (e.g.
 
282
Before you break a relationship, a `BeforeRemovingRelationshipEvent` is sent
 
283
out. You can implement constraints by raising exceptions in the subscriber (e.g.
284
284
prevent members from leaving a group before they do something they have to
285
285
do).
286
286
 
287
 
When you break a relationship, a RelationshipRemovedEvent is sent out.
 
287
When you break a relationship, a `RelationshipRemovedEvent` is sent out::
288
288
 
289
289
    >>> from schooltool.relationship.interfaces \
290
290
    ...         import IBeforeRemovingRelationshipEvent
312
312
Symmetric relationships
313
313
-----------------------
314
314
 
315
 
Symmetric relationships work too:
 
315
Symmetric relationships work too::
316
316
 
317
317
    >>> URIFriendship = URIObject('example:Friendship', 'Friendship')
318
318
    >>> URIFriend = URIObject('example:Friend', 'Friend')
334
334
    [fido, neko]
335
335
 
336
336
Note that if you use symmetric relationships, you cannot use `__getitem__`
337
 
on IBeforeRelationshipEvents.
 
337
on `IBeforeRelationshipEvent`'s.
338
338
 
339
339
 
340
340
Annotated relationships
362
362
    kermit and frogger know each other for years
363
363
 
364
364
Since this is very inconvenient, I expect you will write your own access
365
 
properties that mimic RelationshipProperty, and override the `__iter__`
 
365
properties that mimic `RelationshipProperty`, and override the `__iter__`
366
366
method.  For example:
367
367
 
368
368
    >>> from schooltool.relationship.relationship import BoundRelationshipProperty
379
379
    ...             if link.role == self.other_role and link.rel_type == self.rel_type:
380
380
    ...                 yield link.target, link.extra_info
381
381
 
382
 
You can then use it like this:
 
382
You can then use it like this::
383
383
 
384
384
    >>> class Friend(SomeObject):
385
385
    ...     friends = FriendshipProperty()
406
406
 
407
407
- When you delete objects, you should remove all relationships for those
408
408
  objects.  If you use Zope 3 objects events, you can register the
409
 
  `unrelateOnDeletion` event handler from schooltool.relationship.objectevents.
410
 
  configure.zcml of schooltool.relationship does that.
 
409
  `unrelateOnDeletion` event handler from ``schooltool.relationship.objectevents``.
 
410
  `configure.zcml` of ``schooltool.relationship`` does that.
411
411
 
412
 
- When you copy objects (e.g. using Zope's IObjectCopier), you should take
 
412
- When you copy objects (e.g. using Zope's `IObjectCopier`), you should take
413
413
  care to ensure that you do not duplicate just one half of relationship
414
414
  links.  It is tricky.  locationCopy from zope.location.pickling
415
415
  performs deep copies of all objects that are located within the object you
418
418
  an object that is copied and another object that is not copied.
419
419
 
420
420
  You can register the `unrelateOnCopy` event handler from
421
 
  schooltool.relationship.objectevents to solve *part* of the problem
422
 
  (configure.zcml of schooltool.relationship does that).  This event handler
 
421
  ``schooltool.relationship.objectevents`` to solve *part* of the problem
 
422
  (`configure.zcml` of ``schooltool.relationship`` does that).  This event handler
423
423
  removes all relationships from the copy of the object that you are copying.
424
424
  It does not traverse the whole deply-copied subtree, therefore if you
425
425
  have subobjects that participate in relationships with objects outside of
427
427
 
428
428
  An alternative solution is to disallow copying of objects that may have
429
429
  subobjects related with other objects that are not located within the
430
 
  original object.  To do so, declare and IObjectCopier adapter for the object
 
430
  original object.  To do so, declare and `IObjectCopier` adapter for the object
431
431
  and make the `copyable` method return False.
432
432
 
433
433
 
434
 
Cleaning up
435
 
-----------
 
434
.. Cleaning up
436
435
 
437
436
    >>> zope.event.subscribers = old_subscribers
438
437
    >>> setup.placelessTearDown()