Skip to content

Commit 3694766

Browse files
committed
Tidy schema-only-behaviors.md
1 parent de8f3ab commit 3694766

File tree

1 file changed

+40
-42
lines changed

1 file changed

+40
-42
lines changed
Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
---
22
myst:
33
html_meta:
4-
"description": ""
5-
"property=og:description": ""
6-
"property=og:title": ""
7-
"keywords": ""
4+
"description": "Schema-only behaviors using annotations or attributes for content types in Plone"
5+
"property=og:description": "Schema-only behaviors using annotations or attributes for content types in Plone"
6+
"property=og:title": "Schema-only behaviors using annotations or attributes for content types in Plone"
7+
"keywords": "Plone, schema-only, behaviors, annotations, attributes, content types"
88
---
99

1010
# Schema-only behaviors using annotations or attributes
1111

12-
**Writing behaviors that provide schema fields**
12+
This chapter describes how to write behaviors that provide schema fields.
1313

1414
Oftentimes, we simply want a behavior to be a reusable collection of form fields.
1515
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.
1717
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+
1920

2021
## Using annotations
2122

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.
2627

27-
Weve already seen an example of this factory:
28+
We've already seen an example of this factory.
2829

2930
```xml
3031
<plone:behavior
@@ -36,17 +37,16 @@ We’ve already seen an example of this factory:
3637
/>
3738
```
3839

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+
4244

4345
## Storing attributes
4446

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.
4848

49-
As an example, heres the standard *IRelatedItems* behavior from *plone.app.dexerity*:
49+
As an example, here's the standard `IRelatedItems` behavior from `plone.app.dexerity`.
5050

5151
```xml
5252
<plone:behavior
@@ -57,7 +57,7 @@ As an example, here’s the standard *IRelatedItems* behavior from *plone.app.de
5757
/>
5858
```
5959

60-
The *IRelatedItems* schema looks like this:
60+
The following is the `IRelatedItems` schema.
6161

6262
```python
6363
from plone.autoform.interfaces import IFormFieldProvider
@@ -74,8 +74,8 @@ class IRelatedItems(model.Schema):
7474
"""Behavior interface to make a type support related items.
7575
"""
7676

77-
form.fieldset('categorization', label="Categorization",
78-
fields=['relatedItems'])
77+
form.fieldset("categorization", label="Categorization",
78+
fields=["relatedItems"])
7979

8080
relatedItems = RelationList(
8181
title="Related Items",
@@ -86,31 +86,29 @@ class IRelatedItems(model.Schema):
8686
)
8787
```
8888

89-
This is a standard schema using *plone.autoform.directives*.
89+
This is a standard schema using `plone.autoform.directives`.
9090
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).
9393

9494
This approach has a few advantages:
9595

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 types 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.
102102

103103
The possible disadvantages are:
104104

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.
111111

112-
:::{note}
113-
“The moral of this story? BTrees do not always make things more efficient!” ~ Laurence Rowe
114-
:::
115-
116-
[zope.annotation]: http://pypi.python.org/pypi/zope.annotation
112+
```{note}
113+
"The moral of this story? BTrees do not always make things more efficient!" ~ Laurence Rowe
114+
```

0 commit comments

Comments
 (0)