Fork me on GitHub

Application Model

The application model consists of nodes providing the application hierarchy, security declarations, UI configuration and node type information for authoring.

The base application node utilizes node and implements the following contracts:

  • node.interfaces.INode defines the basic tree information and containment API used for application model traversal.

  • node.interfaces.IAttributes extends the node by an attrs property which holds the node data. We use a dedicated object holding the attributes in order to separate node hierarchy information from actual node data in a clean way.

  • extends the node by the __acl__ property. It is used by Pyramid at publishing time to manage access. See Security documentation for more details.

  • extends the node by application specific contracts.

    • properties: Property containing implementing object. The properties are supposed to provide UI configuration settings. See UI widgets for details.
    • metadata: Property containing implementing object. Metadata is used by different UI widgets to display node metadata.
    • nodeinfo: Property containing implementing object. NodeInfo provides cardinality information and general node information which is primary needed for authoring operations.


The implements all required aspects for representing an application node. It can be used as starting point when writing application models.

from import BaseNode

class CustomNode(BaseNode):
    """Model related code goes here.

A more advanced technique is to use the mechanisms provided by plumber package directly for defining different behaviors of a node. The IApplicationNode related interface extensions are implemented as plumbing behaviors as well, and the BaseNode class is just a combination of plumbing behaviors without extending anything on the class directly.

from import AppNode
from node import behaviors
from plumber import plumbing

class AdvancedNode(object)
    """The used plumbing behaviors on this class are the same as for


The can be used to serve fixed children. The factory node provides a factory attribute containing a dict, where the keys represent the available children keys, and the values are callables which act as factory for given key on first access.

E.g., the AppRoot node inherits from FactoryNode and is used to serve the entry nodes of the application.

from import FactoryNode

class CustomFactoryNode(FactoryNode):
    factories = {
        'child_by_factory_function': self.child_factory_function,
        'child_by_node_init_as_factory': BaseNode,

    def child_factory_function(self):
        return BaseNode()


The can be used for publishing nodes of models where the hierarchy differs from the one of the application model.

The adapter node by default acts as proxy for __iter__ and attrs, all other functions refer to the underlying node.behaviors.OdictStorage of the adapter node.

If an adapter node wants to publish the children of the adapted node, it must not do this by just returning the children of the adapted node because the application node hierarchy would get invalid. Thus it is required to adapt them as well. Do this by overrwriting __getitem__.

from import AdapterNode

class AdaptedChildNode(AdapterNode):

class CustomAdapterNode(AdapterNode):

    def __getitem__(self, key):
        except KeyError:
            child_context = self.model[key]
            child = AdaptedChildNode(child_context, key, self)
  [key] = child
            return child

AppRoot derives from FactoryNode and represents the application model root node.

This node gets instanciated only once on application startup. Every plugin entry point registered with register_entry gets written to the factories attribute of the root node.

Root node related settings from the .ini file are written to properties respective metadata objects of the application root node.

The root node can be accessed either by calling self.root on application model nodes or by using utility.

from import get_root

root = get_root()

AppSettings is like application root a factory node initialized at application startup. Every settings node factory registered with register_config gets written to the factories attribute of the settings node.

The settings node provides relevant properties and metadata objects and an __acl__ restricting access to the manager role.

The settings node is available at settings on application model root.

settings = get_root()['settings']

CopySupport is a plumbing behavior for application model nodes indicating that it’s children can be cut and copied, and that nodes from another subtree can be pasted. Cut, copy and paste features are controlled by supports_cut, supports_copy respective supports_paste flags. They all default to True.

NamespaceUUID is a plumbing behavior for application model nodes implementing node.interfaces.IUUID. The uuid attribute is implemented as read-only property which computes the UUID as uuid.uuid5 from a namespace and the node path. The uuid namespace can be overwritten via uuid_namespace attribute.

UUIDAttributeAware is a plumbing behavior and supposed to be used to expose self.attrs['uuid'] at self.uuid.



EXPERIMENTAL - Subject to change. is a plumbing behavior which provides self.uuid at In conjunction with UUIDAttributeAware it is possible to create application models where nodes are traversable by persistent UUID.

self.set_uuid_for(node, override=False, recursiv=False) can be used to recursively update UUID’s on copies of a node.

Properties can be used for any kind of property mapping. The contract is described in The application node attributes properties and metadata promise to provide an IProperties implementation.

Properties are accessed via python attribute access, but never raise an AttributeError if property not exists, instead None is returned.

Available properties are provided by keys function.


Although one Python ZEN rule says “Explicit is better than implicit”, the behavior is desired.

The reason is that IProperties objects are used to expect UI element settings or metadata on application nodes.

When writing new UI elements supporting custom settings it’s not necessary to extend the properties objects all the time but just add the desired new setting to it.

The other way around a UI element accessing a missing setting property can consider the UI element unconfigured/unavailable if expected setting is None.

The downside of this strategy is that it’s necessary to be careful when defining setting names. They need to be explicit enough to avoid namespace clashes between UI widgets. A good practice is to prefix widget related settings by the related tile name.

>>> from import Properties

>>> props = Properties
>>> props.a = '1'
>>> props.b = '2'
>>> props.keys()
['a', 'b']

>>> assert(props.a == '1')
>>> assert(props.not_exists is None)

ProtectedProperties object can be used to secure property access by permissions. Properties with no permissions are always returned. See Security documentation for more details about permissions.

from import ProtectedProperties

Define the permission map. In this example, permission ‘view’ is required to access property ‘a’, and permission ‘edit’ is required to access property ‘b’.

permissions = {
    'a': ['view'],
    'b': ['edit'],

The model to check the permissions against.

model = BaseNode()

Property data.

data = {
    'a': '1',  # 'view' permission protected
    'b': '2',  # 'edit' permission protected
    'c': '3',  # unprotected

Initialize properties.

props = ProtectedProperties(model, permissions, data)

If a user does not have the required permission granted to access a specific property, ProtectedProperties behaves as if this property is inexistent.


Write access to properties is not protected at all.

Metadata class inherits from and adds the marker interface This object is for

XMLProperties is an IProperties implementation which can be used to serialize/deserialize properties to XML files. Supported value types are string, list, tuple, dict and datetime.datetime.

from import XMLProperties

file = '/path/to/file.xml'
props = XMLProperties(file)
props.a = '1'
props()  # persist to file

ConfigProperties is an IProperties implementation which can be used to serialize/deserialize properties to .ini files.

Property values are handled as unicode strings and get UTF-8 encoded. It’s possible to change the encoding by settings the encoding attribute.

By default the properties are stored in the properties section of the .ini file. This can be configured by setting the properties_section attribute.

The constructor expects the file path and an optional data dictionary containing initial properties as arguments.

from import ConfigProperties

props = ConfigProperties(
props.b = u'b'
props()  # persist to file

NodeInfo class inherits from and adds the marker interface

NodeInfo provides cardinality information and general node information which is primary needed for authoring operations. The following properties are used:

  • name: Unique name as string of node type.
  • title: Title of this node type.
  • description: Description of this node type.
  • factory: Add model factory. Function used to instanciate a non persistent instance of node type used to render add forms. Defaults to
  • addables: List of node info names. Defines which node types are allowed as children in this node.
  • icon: Icon for node type. Icon support is implemented using icon fonts. Ionicons are shipped and delivered with by default.

NodeInfo objects are not instanciated directly, instead the decorator is used to register node types.

from import BaseNode
from import node_info

    title='Custom Node',
    description='A Custom Node',
class CustomNode(BaseNode):

The NodeInfo instance can be accessed either on the application model nodes or with

get_node_info returns None if node info by name not exists while model.nodeinfo always returns a NodeInfo instance, regardless whether there has been registered a dedicated one or not.

from import get_node_info

# lookup node info by utility function
info = get_node_info('custom_node')

# lookup node info from model
model = CustomNode()
info = model.nodeinfo

See Forms documentation for more details.