File: sources.rst

package info (click to toggle)
zope.schema 8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 736 kB
  • sloc: python: 6,957; makefile: 130
file content (107 lines) | stat: -rw-r--r-- 3,127 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
=======
Sources
=======

Concepts
--------

Sources are designed with three concepts:

- The source itself - an iterable

  This can return any kind of object it wants. It doesn't have to care
  for browser representation, encoding, ...

- A way to map a value from the iterable to something that can be used
  for form *values* - this is called a token. A token is commonly a
  (unique) 7bit representation of the value.

- A way to map a value to something that can be displayed to the user -
  this is called a title

The last two elements are dispatched using a so called `term`. The
ITitledTokenizedTerm interface contains a triple of (value, token, title).

Additionally there are some lookup functions to perform the mapping
between values and terms and tokens and terms.

Sources that require context use a special factory: a context source
binder that is called with the context and instanciates the source when
it is actually used.

Sources in Fields
-----------------

A choice field can be constructed with a source or source name.  When a source
is used, it will be used as the source for valid values.

Create a source for all odd numbers.

.. doctest::

   >>> from zope import interface
   >>> from zope.schema.interfaces import ISource, IContextSourceBinder
   >>> @interface.implementer(ISource)
   ... class MySource(object):
   ...     divisor = 2
   ...     def __contains__(self, value):
   ...         return bool(value % self.divisor)
   >>> my_source = MySource()
   >>> 1 in my_source
   True
   >>> 2 in my_source
   False

   >>> from zope.schema import Choice
   >>> choice = Choice(__name__='number', source=my_source)
   >>> bound = choice.bind(object())
   >>> bound.vocabulary
   <...MySource...>

If a IContextSourceBinder is passed as the `source` argument to Choice, it's
`bind` method will be called with the context as its only argument.   The
result must implement ISource and will be used as the source.

.. doctest::

   >>> _my_binder_called = []
   >>> def my_binder(context):
   ...     _my_binder_called.append(context)   
   ...     source = MySource()
   ...     source.divisor = context.divisor
   ...     return source
   >>> interface.directlyProvides(my_binder, IContextSourceBinder)

   >>> class Context(object):
   ...     divisor = 3

   >>> choice = Choice(__name__='number', source=my_binder)
   >>> bound = choice.bind(Context())
   >>> len(_my_binder_called)
   1
   >>> bound.vocabulary
   <...MySource...>
   >>> bound.vocabulary.divisor
   3

When using IContextSourceBinder together with default value, it's
impossible to validate it on field initialization. Let's check if
initalization doesn't fail in that case.

.. doctest::

   >>> choice = Choice(__name__='number', source=my_binder, default=2)

   >>> del _my_binder_called[:]
   >>> bound = choice.bind(Context())
   >>> len(_my_binder_called)
   1

   >>> bound.validate(bound.default)
   >>> bound.validate(3)
   Traceback (most recent call last):
   ...
   ConstraintNotSatisfied: 3

It's developer's responsibility to provide a default value that fits the
constraints when using context-based sources.