summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki/basiclayout.rst
blob: 7fe7daac853784d24bf1c56d772d582db8c6eeb1 (plain)
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
============
Basic Layout
============

The starter files generated by the ``pyramid_zodb`` template are basic, but
they provide a good orientation for the high-level patterns common to most
:term:`traversal` -based :app:`Pyramid` (and :term:`ZODB` based) projects.

The source code for this tutorial stage can be browsed via
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/
<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/>`_.

App Startup with ``__init__.py``
--------------------------------

A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file.  Even if empty, this marks a directory as a Python
package.  Our application uses ``__init__.py`` as both a package marker, as
well as to contain application configuration code.

When you run the application using the ``paster`` command using the
``development.ini`` generated config file, the application configuration
points at an Setuptools *entry point* described as ``egg:tutorial``.  In our
application, because the application's ``setup.py`` file says so, this entry
point happens to be the ``main`` function within the file named
``__init__.py``:

   .. literalinclude:: src/basiclayout/tutorial/__init__.py
      :linenos:
      :language: py

#. *Lines 1-3*.  Perform some dependency imports.

#. *Line 8*. Get the ZODB configuration from the ``development.ini``
   file's ``[app:main]`` section represented by the ``settings``
   dictionary passed to our ``app`` function.  This will be a URI
   (something like ``file:///path/to/Data.fs``).

#. *Line 12*. We create a "finder" object using the
   ``PersistentApplicationFinder`` helper class, passing it the ZODB
   URI and the "appmaker" we've imported from ``models.py``.

#. *Lines 13 - 14*.  We create a :term:`root factory` which uses the
   finder to return a ZODB root object.

#. *Line 15*.  We construct a :term:`Configurator` with a :term:`root
   factory` and the settings keywords parsed by PasteDeploy.  The root
   factory is named ``get_root``.

#. *Line 16*.  Register a 'static view' which answers requests which start
   with with URL path ``/static`` using the
   :meth:`pyramid.config.Configurator.add_static_view method`.  This
   statement registers a view that will serve up static assets, such as CSS
   and image files, for us, in this case, at
   ``http://localhost:6543/static/`` and below.  The first argument is the
   "name" ``static``, which indicates that the URL path prefix of the view
   will be ``/static``.  the The second argument of this tag is the "path",
   which is an :term:`asset specification`, so it finds the resources it
   should serve within the ``static`` directory inside the ``tutorial``
   package.

#. *Line 17*.  Perform a :term:`scan`.  A scan will find :term:`configuration
   decoration`, such as view configuration decorators
   (e.g. ``@view_config``) in the source code of the ``tutorial`` package and
   will take actions based on these decorators.  The argument to
   :meth:`~pyramid.config.Configurator.scan` is the package name to scan,
   which is ``tutorial``.

#. *Line 18*.  Use the
   :meth:`pyramid.config.Configurator.make_wsgi_app` method
   to return a :term:`WSGI` application.

Resources and Models with ``models.py``
---------------------------------------

:app:`Pyramid` uses the word :term:`resource` to describe objects arranged
hierarchically in a :term:`resource tree`.  This tree is consulted by
:term:`traversal` to map URLs to code.  In this application, the resource
tree represents the site structure, but it *also* represents the
:term:`domain model` of the application, because each resource is a node
stored persistently in a :term:`ZODB` database.  The ``models.py`` file is
where the ``pyramid_zodb`` Paster template put the classes that implement our
resource objects, each of which happens also to be a domain model object.

Here is the source for ``models.py``:

   .. literalinclude:: src/basiclayout/tutorial/models.py
      :linenos:
      :language: py

#. *Lines 3-4*.  The ``MyModel`` :term:`resource` class is implemented here.
   Instances of this class will be capable of being persisted in :term:`ZODB`
   because the class inherits from the
   :class:`persistent.mapping.PersistentMapping` class.  The ``__parent__``
   and ``__name__`` are important parts of the :term:`traversal` protocol.
   By default, have these as ``None`` indicating that this is the
   :term:`root` object.

#. *Lines 6-12*.  ``appmaker`` is used to return the *application
   root* object.  It is called on *every request* to the
   :app:`Pyramid` application.  It also performs bootstrapping by
   *creating* an application root (inside the ZODB root object) if one
   does not already exist.
 
   We do so by first seeing if the database has the persistent
   application root.  If not, we make an instance, store it, and
   commit the transaction.  We then return the application root
   object.

Views With ``views.py``
-----------------------

Our paster template generated a default ``views.py`` on our behalf.  It
contains a single view, which is used to render the page shown when you visit
the URL ``http://localhost:6543/``.

Here is the source for ``views.py``:

   .. literalinclude:: src/basiclayout/tutorial/views.py
      :linenos:
      :language: py

Let's try to understand the components in this module:

#. *Lines 1-2*. Perform some dependency imports.

#. *Line 4*.  Use the :func:`pyramid.view.view_config` :term:`configuration
   decoration` to perform a :term:`view configuration` registration.  This
   view configuration registration will be activated when the application is
   started.  It will be activated by virtue of it being found as the result
   of a :term:`scan` (when Line 17 of ``__init__.py`` is run).

   The ``@view_config`` decorator accepts a number of keyword arguments.  We
   use two keyword arguments here: ``context`` and ``renderer``.

   The ``context`` argument signifies that the decorated view callable should
   only be run when :term:`traversal` finds the ``tutorial.models.MyModel``
   :term:`resource` to be the :term:`context` of a request.  In English, this
   means that when the URL ``/`` is visited, because ``MyModel`` is the root
   model, this view callable will be invoked.

   The ``renderer`` argument names an :term:`asset specification` of
   ``tutorial:templates/mytemplate.pt``.  This asset specification points at
   a :term:`Chameleon` template which lives in the ``mytemplate.pt`` file
   within the ``templates`` directory of the ``tutorial`` package.  And
   indeed if you look in the ``templates`` directory of this package, you'll
   see a ``mytemplate.pt`` template file, which renders the default home page
   of the generated project.

   Since this call to ``@view_config`` doesn't pass a ``name`` argument, the
   ``my_view`` function which it decorates represents the "default" view
   callable used when the context is of the type ``MyModel``.

#. *Lines 5-6*.  We define a :term:`view callable` named ``my_view``, which
   we decorated in the step above.  This view callable is a *function* we
   write generated by the ``pyramid_zodb`` template that is given a
   ``request`` and which returns a dictionary.  The ``mytemplate.pt``
   :term:`renderer` named by the asset specification in the step above will
   convert this dictionary to a :term:`response` on our behalf.

   The function returns the dictionary ``{'project':'tutorial'}``.  This
   dictionary is used by the template named by the ``mytemplate.pt`` asset
   specification to fill in certain values on the page.

The WSGI Pipeline in ``development.ini``
----------------------------------------

The ``development.ini`` (in the tutorial :term:`project` directory, as
opposed to the tutorial :term:`package` directory) looks like this:

.. literalinclude:: src/views/development.ini
   :language: ini


Note the existence of a ``[pipeline:main]`` section which specifies our WSGI
pipeline.  This "pipeline" will be served up as our WSGI application.  As far
as the WSGI server is concerned the pipeline *is* our application.  Simpler
configurations don't use a pipeline: instead they expose a single WSGI
application as "main".  Our setup is more complicated, so we use a pipeline
composed of :term:`middleware`.

The ``egg:WebError#evalerror`` middleware is at the "top" of the pipeline.
This is middleware which displays debuggable errors in the browser while
you're developing (not recommended for deployment).

The ``egg:repoze.zodbconn#closer`` middleware is in the middle of the
pipeline.  This is a piece of middleware which closes the ZODB connection
opened by the ``PersistentApplicationFinder`` at the end of the request.

The ``egg:repoze.retry#retry`` middleware catches ``ConflictError``
exceptions from ZODB and retries the request up to three times (ZODB is an
optimistic concurrency database that relies on application-level transaction
retries when a conflict occurs).

The ``tm`` middleware is the last piece of middleware in the pipeline.  This
commits a transaction near the end of the request unless there's an exception
raised or the HTTP response code is an error code.  The ``tm`` refers to the
``[filter:tm]`` section beneath the pipeline declaration, which configures
the transaction manager.

The final line in the ``[pipeline:main]`` section is ``tutorial``, which
refers to the ``[app:tutorial]`` section above it.  The ``[app:tutorial]``
section is the section which actually defines our application settings.  The
values within this section are passed as ``**settings`` to the ``main``
function we defined in ``__init__.py`` when the server is started via
``paster serve``.