You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# Schema-only behaviors using annotations or attributes
11
11
12
-
**Writing behaviors that provide schema fields**
12
+
This chapter describes how to write behaviors that provide schema fields.
13
13
14
14
Oftentimes, we simply want a behavior to be a reusable collection of form fields.
15
15
Integrators can then compose their types by combining different schemata.
16
-
Writing the behavior schema is no different to writing any other schema interface.
16
+
Writing the behavior schema is no different than writing any other schema interface.
17
17
But how and where do we store the values?
18
-
By default, *plone.behavior* provides two alternatives.
18
+
By default, `plone.behavior` provides two alternatives.
19
+
19
20
20
21
## Using annotations
21
22
22
-
Annotations, as provided by the [zope.annotation] package, are a standard means of storing of key/value pairs on objects.
23
-
In the default implementation (so-called *attribute annotation*), the values are stored in a BTree on the object called *\_\_annotations\_\_*.
24
-
The raw annotations API involves adapting the object to the *IAnnotations* interface, which behaves like a dictionary, and storing values under unique keys here.
25
-
*plone.behavior* comes with a special type of factory that means you can simply adapt an object to its behavior interface to get an adapter providing this interface, on which you can get and set values, which are eventually stored in annotations.
23
+
Annotations, as provided by the [`zope.annotation`](https://pypi.org/project/zope.annotation/) package, are a standard means of storing of key/value pairs on objects.
24
+
In the default implementation (so-called `attribute annotation`), the values are stored in a BTree on the object called `__annotations__`.
25
+
The raw annotations API involves adapting the object to the `IAnnotations` interface, which behaves like a dictionary, and storing values under unique keys here.
26
+
`plone.behavior` comes with a special type of factory that lets you adapt an object to its behavior interface to get an adapter providing this interface, on which you can get and set values, which are eventually stored in annotations.
26
27
27
-
We’ve already seen an example of this factory:
28
+
We've already seen an example of this factory.
28
29
29
30
```xml
30
31
<plone:behavior
@@ -36,17 +37,16 @@ We’ve already seen an example of this factory:
36
37
/>
37
38
```
38
39
39
-
Here, *plone.behavior.AnnotationStorage* is a behavior factory that can be used by any behavior with an interface that consists entirely of *zope.schema* fields.
40
-
It simply stores those items in object annotations, saving you the trouble of writing your own annotation storage adapter.
41
-
If you adapt an object for which the behavior is enabled to the behavior interface, you will be able to read and write values off the resultant adapter as normal.
40
+
Here `plone.behavior.AnnotationStorage` is a behavior factory that can be used by any behavior with an interface that consists entirely of `zope.schema` fields.
41
+
It stores those items in object annotations, saving you the trouble of writing your own annotation storage adapter.
42
+
If you adapt an object for which the behavior is enabled to the behavior interface, you will be able to read and write values off the resultant adapter as usual.
43
+
42
44
43
45
## Storing attributes
44
46
45
-
This approach is convenient, but there is another approach that is even more convenient, and, contrary to what you may think, may be more
46
-
efficient:
47
-
simply store the attributes of the schema interface directly on the content object.
47
+
This approach is convenient, but there is another approach that is even more convenient, and, contrary to what you may think, may be more efficient: store the attributes of the schema interface directly on the content object.
48
48
49
-
As an example, here’s the standard *IRelatedItems* behavior from *plone.app.dexerity*:
49
+
As an example, here's the standard `IRelatedItems` behavior from `plone.app.dexerity`.
50
50
51
51
```xml
52
52
<plone:behavior
@@ -57,7 +57,7 @@ As an example, here’s the standard *IRelatedItems* behavior from *plone.app.de
57
57
/>
58
58
```
59
59
60
-
The *IRelatedItems* schema looks like this:
60
+
The following is the `IRelatedItems` schema.
61
61
62
62
```python
63
63
from plone.autoform.interfaces import IFormFieldProvider
@@ -74,8 +74,8 @@ class IRelatedItems(model.Schema):
74
74
"""Behavior interface to make a type support related items.
@@ -86,31 +86,29 @@ class IRelatedItems(model.Schema):
86
86
)
87
87
```
88
88
89
-
This is a standard schema using *plone.autoform.directives*.
89
+
This is a standard schema using `plone.autoform.directives`.
90
90
However, notice the lack of a behavior factory.
91
-
This is a directly provided “marker” interface, except that it has attributes, and so it is not actually a marker interface.
92
-
The result is that the *relatedItems* attribute will be stored directly onto a content object when first set (usually in the add form).
91
+
This is a directly provided "marker" interface, except that it has attributes, and so it is not actually a marker interface.
92
+
The result is that the `relatedItems` attribute will be stored directly onto a content object when first set (usually in the add form).
93
93
94
94
This approach has a few advantages:
95
95
96
-
- There is no need to write or use a separate factory, so it is a little easier to use.
97
-
- The attribute is available on the content object directly, so you can write *context/relatedItems* in a TAL expression, for example.
98
-
This does require that it has been set at least once, though!
99
-
If the schema is used in the type’s add form, that will normally suffice, but old instances of the same type may not have the attribute and could raise an *AttributeError.*
100
-
- If the value is going to be used frequently, and especially if it is read when viewing the content object, storing it in an attribute is more efficient than storing it in an annotation.
101
-
Background: This is because the *\_\_annotations\_\_* BTree is a separate persistent object which has to be loaded into memory, and may push something else out of the ZODB cache.
96
+
-There is no need to write or use a separate factory, so it is a little easier to use.
97
+
-The attribute is available on the content object directly, so you can write `context/relatedItems` in a TAL expression, for example.
98
+
This does require that it has been set at least once, though.
99
+
If the schema is used in the type's add form, that will normally suffice, but old instances of the same type may not have the attribute and could raise an `AttributeError.`
100
+
-If the value is going to be used frequently, and especially if it is read when viewing the content object, storing it in an attribute is more efficient than storing it in an annotation.
101
+
This is because the `__annotations__` BTree is a separate persistent object which has to be loaded into memory, and may push something else out of the ZODB cache.
102
102
103
103
The possible disadvantages are:
104
104
105
-
- The attribute name may collide with another attribute on the object, either from its class, its base schema, or another behavior.
106
-
Whether this is a problem in practice depends largely on whether the name is likely to be unique.
107
-
In most cases, it will probably be sufficiently unique.
108
-
- If the attribute stores a large value, it will increase memory usage, as it will be loaded into memory each time the object is fetched from the ZODB.
109
-
However, you should use BLOBs to store large values and BTrees to store many values anyway.
110
-
Loading an object with a BLOB or BTree does not mean loading the entire data, so the memory overhead does not occur unless the whole BLOB or BTree is actually used.
105
+
-The attribute name may collide with another attribute on the object, either from its class, its base schema, or another behavior.
106
+
Whether this is a problem in practice depends largely on whether the name is likely to be unique.
107
+
In most cases, it will probably be sufficiently unique.
108
+
-If the attribute stores a large value, it will increase memory usage, as it will be loaded into memory each time the object is fetched from the ZODB.
109
+
However, you should use blob to store large values and BTrees to store many values anyway.
110
+
Loading an object with a blob or BTree does not mean loading the entire data, so the memory overhead does not occur unless the whole blob or BTree is actually used.
111
111
112
-
:::{note}
113
-
“The moral of this story? BTrees do not always make things more efficient!” ~ Laurence Rowe
0 commit comments