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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
Models
======
A :term:`model` is typically a simple Python class defined in a
module. Model *instances* make up the graph that :mod:`repoze.bfg` is
willing to traverse.
Defining a Model
----------------
Here's an example of a model describing a blog entry:
.. code-block:: python
:linenos:
import datetime
class BlogEntry(object):
def __init__(self, title, body, author):
self.title = title
self.body = body
self.author = author
self.created = datetime.datetime.now()
A model may be essentially any Python object. In the above example,
an instance of the ``BlogEntry`` class can be created and used as a
model.
Models Which Implement Interfaces
---------------------------------
Models can optionally be made to implement an :term:`interface`. This
makes it possible to register views against the interface itself
instead of the *class* within view statement in the application
registry. For example, here's some code which describes a blog entry
whicg also declares that the blog entry implements an
:term:`interface`.
.. code-block:: python
:linenos:
import datetime
from zope.interface import implements
from zope.interface import Interface
class IBlogEntry(Interface):
pass
class BlogEntry(object):
implements(IBlogEntry)
def __init__(self, title, body, author):
self.title = title
self.body = body
self.author = author
self.created = datetime.datetime.now()
This model consists of two things: the object which defines the model
(above as the class ``BlogEntry``), and an :term:`interface` attached
to the model object (above as the class ``IBlogEntry``). An interface
simply tags the model object with a "type" that can be referred to
within the :term:`application registry`. A model object can implement
zero or more interfaces. The interface must be an instance of a class
that inherits from ``zope.interface.Interface``.
You specify that a model *implements* an interface by using the
``zope.interface.implements`` function at class scope. The above
``BlogEntry`` model implements the ``IBlogEntry`` interface.
See the :ref:`views_chapter` for more information about why providing
models with an interface can be an interesing thing to do.
Defining a Graph of Model Instances
-----------------------------------
:mod:`repoze.bfg` expects to be able to traverse a graph of model
instances. :mod:`repoze.bfg` imposes the following policy on model
instance nodes in the graph:
- Nodes which contain other nodes (aka "container" nodes) must supply
a ``__getitem__`` method which is willing to resolve a unicode name
to a subobject. If a subobject by that name does not exist in the
container, ``__getitem__`` must raise a ``KeyError``. If a
subobject by that name *does* exist, the container should return the
subobject (another model instance).
- Nodes which do not contain other nodes (aka "leaf" nodes) must not
implement a ``__getitem__``, or if they do, their ``__getitem__``
method must raise a ``KeyError``.
.. _location_aware:
Location-Aware Model Instances
------------------------------
In order for :mod:`repoze.bfg` location, security, URL-generation, and
traversal functions (such as the functions exposed in
:ref:`location_module`, :ref:`traversal_module`, and :ref:`url_module`
as well as certain functions in :ref:`security_module` ) to work
properly against a instances in a model graph, all nodes in the graph
must be "location-aware". This means they must have two attributes:
``__parent__`` and ``__name__``. The ``__parent__`` attribute should
be a reference to the node's parent model instance in the graph. The
``__name__`` attribute should be the name that a node's parent refers
to the node via ``__getitem__``. The ``__parent__`` of the root
object should be ``None`` and its ``__name__`` should be the empty
string. For instance:
.. code-block:: python
class MyRootObject(object):
__name__ = ''
__parent__ = None
.. note:: If your root model object has a ``__name__`` argument that
is not ``None`` or the empty string, URLs returned by the
``repoze.bfg.url.model_url`` function and paths generated by the
``repoze.bfg.traversal.model_path`` and
``repoze.bfg.traversal.model_path_tuple`` APIs will be generated
improperly. The value of ``__name__`` will be prepended to every
path and URL generated (as opposed to a single leading slash or
empty tuple element).
A node returned from the root item's ``__getitem__`` method should
have a ``__parent__`` attribute that is a reference to the root
object, and its ``__name__`` attribute should match the name by which
it is are reachable via the root object's ``__getitem__``. *That*
object's ``__getitem__`` should return objects that have a
``__parent__`` attribute that points at that object, and
``__getitem__``-returned objects should have a ``__name__`` attribute
that matches the name by which they are retrieved via ``__getitem__``,
and so on.
.. note::
If you'd rather not manage the ``__name__`` and ``__parent__``
attributes of your models "by hand", an add-on package to
:mod:`repoze.bfg`` named :mod:`repoze.bfg.traversalwrapper` can help
you do this.
In order to use this helper feature, you must first install the
:mod:`repoze.bfg.traversalwrapper` package (available from
`http://svn.repoze.org/repoze.bfg.traversalwrapper
<http://svn.repoze.org/repoze.bfg.traversalwrapper>`_), then
register its ``ModelGraphTraverser`` as the traversal policy, rather
than the default BFG ``ModelGraphTraverser``. To register the
:mod:`repoze.bfg.traversalwrapper` ``ModelGraphTraverser`` as the
traversal policy, your application will need to have the following
in its ``configure.zcml`` file:
.. code-block:: xml
<adapter
factory="repoze.bfg.traversalwrapper.ModelGraphTraverser"
provides="repoze.bfg.interfaces.ITraverserFactory"
for="*"
/>
If this statement is made in ZCML, you will no longer need to manage
the ``__parent__`` and ``__name__`` attributes on graph objects "by
hand". Instead, as necessary, during traversal :mod:`repoze.bfg`
will wrap each object (even the root object) in a ``LocationProxy``
which will dynamically assign a ``__name__`` and a ``__parent__`` to
the traversed object (based on the last traversed object and the
name supplied to ``__getitem__``). The root object will have a
``__name__`` attribute of ``None`` and a ``__parent__`` attribute
of ``None``.
:mod:`repoze.bfg` API Functions That Act Against Models
-------------------------------------------------------
A model instance is used as the :term:`context` argument provided to a
view. See :ref:`traversal_chapter` for more information about how a
model becomes the context.
The APIs provided by :ref:`traversal_module` are used against model
instances. These functions can be used to find the "path" of a model,
find the URL of a model, the root model in a model graph, and so on.
The APIs provided by :ref:`location_module` are used against model
instances. These can be used to walk down a model graph, or
conveniently locate one object "inside" another.
Some APIs in :ref:`security_module` accept a model object as a
parameter. For example, the ``has_permission`` API accepts a
"context" (a model object) as one of its arguments; the "acl" is
obtained from this model or one of its ancestors. Other APIs in the
same module also accept :term:`context` as an argument, and a context
is always a model.
|