commit | author | age
|
93a7a1
|
1 |
.. _wiki_basic_layout: |
SP |
2 |
|
e53e13
|
3 |
============ |
CM |
4 |
Basic Layout |
|
5 |
============ |
|
6 |
|
beb4f1
|
7 |
The starter files generated by the ``zodb`` cookiecutter are very basic, but |
b743bb
|
8 |
they provide a good orientation for the high-level patterns common to most |
34a913
|
9 |
:term:`traversal`-based (and :term:`ZODB`-based) :app:`Pyramid` projects. |
e53e13
|
10 |
|
CM |
11 |
|
34a913
|
12 |
Application configuration with ``__init__.py`` |
SP |
13 |
---------------------------------------------- |
e53e13
|
14 |
|
b3b713
|
15 |
A directory on disk can be turned into a Python :term:`package` by containing |
CM |
16 |
an ``__init__.py`` file. Even if empty, this marks a directory as a Python |
f5a9a5
|
17 |
package. We use ``__init__.py`` both as a marker, indicating the directory in |
SP |
18 |
which it's contained is a package, and to contain application configuration |
34a913
|
19 |
code. |
b3b713
|
20 |
|
cfb2b5
|
21 |
When you run the application using the ``pserve`` command using the |
34a913
|
22 |
``development.ini`` generated configuration file, the application |
f5a9a5
|
23 |
configuration points at a setuptools *entry point* described as |
34a913
|
24 |
``egg:tutorial``. In our application, because the application's ``setup.py`` |
SP |
25 |
file says so, this entry point happens to be the ``main`` function within the |
f5a9a5
|
26 |
file named ``__init__.py``. |
b3b713
|
27 |
|
f5a9a5
|
28 |
Open ``tutorial/__init__.py``. It should already contain the following: |
SP |
29 |
|
|
30 |
.. literalinclude:: src/basiclayout/tutorial/__init__.py |
|
31 |
:linenos: |
|
32 |
:language: py |
b3b713
|
33 |
|
c44c40
|
34 |
#. *Lines 1-3*. Perform some dependency imports. |
b3b713
|
35 |
|
34a913
|
36 |
#. *Lines 6-8*. Define a :term:`root factory` for our Pyramid application. |
b3b713
|
37 |
|
34a913
|
38 |
#. *Line 11*. ``__init__.py`` defines a function named ``main``. |
SP |
39 |
|
|
40 |
#. *Line 14*. We construct a :term:`Configurator` with a root |
|
41 |
factory and the settings keywords parsed by :term:`PasteDeploy`. The root |
46d17b
|
42 |
factory is named ``root_factory``. |
b3b713
|
43 |
|
909ae0
|
44 |
#. *Lines 15 and 16*. Get the settings and use an explicit transaction transaction manager for apps so that they do not implicitly create new transactions when touching the manager outside of the ``pyramid_tm`` lifecycle. |
SP |
45 |
|
|
46 |
#. *Line 17*. Include support for the :term:`Chameleon` template rendering |
404b28
|
47 |
bindings, allowing us to use the ``.pt`` templates. |
MM |
48 |
|
909ae0
|
49 |
#. *Line 18*. Include support for ``pyramid_tm``, allowing Pyramid requests to join the active transaction as provided by the `transaction <https://pypi.python.org/pypi/transaction>`_ package. |
beb4f1
|
50 |
|
909ae0
|
51 |
#. *Line 19*. Include support for ``pyramid_retry`` to retry a request when transient exceptions occur. |
beb4f1
|
52 |
|
909ae0
|
53 |
#. *Line 20*. Include support for ``pyramid_zodbconn``, providing integration between :term:`ZODB` and a Pyramid application. |
SP |
54 |
|
|
55 |
#. *Line 21*. Register a "static view", which answers requests whose URL |
34a913
|
56 |
paths start with ``/static``, using the |
8aa8a8
|
57 |
:meth:`pyramid.config.Configurator.add_static_view` method. This |
2a5ae0
|
58 |
statement registers a view that will serve up static assets, such as CSS |
CM |
59 |
and image files, for us, in this case, at |
|
60 |
``http://localhost:6543/static/`` and below. The first argument is the |
|
61 |
"name" ``static``, which indicates that the URL path prefix of the view |
08c221
|
62 |
will be ``/static``. The second argument of this tag is the "path", |
0694f0
|
63 |
which is a relative :term:`asset specification`, so it finds the resources |
CM |
64 |
it should serve within the ``static`` directory inside the ``tutorial`` |
beb4f1
|
65 |
package. Alternatively the cookiecutter could have used an *absolute* asset |
49d823
|
66 |
specification as the path (``tutorial:static``). |
b3b713
|
67 |
|
beb4f1
|
68 |
#. *Line 19*. Perform a :term:`scan`. A scan will find :term:`configuration |
49d823
|
69 |
decoration`, such as view configuration decorators (e.g., ``@view_config``) |
0694f0
|
70 |
in the source code of the ``tutorial`` package and will take actions based |
CM |
71 |
on these decorators. We don't pass any arguments to |
|
72 |
:meth:`~pyramid.config.Configurator.scan`, which implies that the scan |
|
73 |
should take place in the current package (in this case, ``tutorial``). |
beb4f1
|
74 |
The cookiecutter could have equivalently said ``config.scan('tutorial')``, but |
0694f0
|
75 |
it chose to omit the package name argument. |
2a5ae0
|
76 |
|
beb4f1
|
77 |
#. *Line 20*. Use the |
d7f259
|
78 |
:meth:`pyramid.config.Configurator.make_wsgi_app` method |
b3b713
|
79 |
to return a :term:`WSGI` application. |
e53e13
|
80 |
|
34a913
|
81 |
Resources and models with ``models.py`` |
2a5ae0
|
82 |
--------------------------------------- |
e53e13
|
83 |
|
a5ffd6
|
84 |
:app:`Pyramid` uses the word :term:`resource` to describe objects arranged |
CM |
85 |
hierarchically in a :term:`resource tree`. This tree is consulted by |
|
86 |
:term:`traversal` to map URLs to code. In this application, the resource |
|
87 |
tree represents the site structure, but it *also* represents the |
b743bb
|
88 |
:term:`domain model` of the application, because each resource is a node |
a5ffd6
|
89 |
stored persistently in a :term:`ZODB` database. The ``models.py`` file is |
beb4f1
|
90 |
where the ``zodb`` cookiecutter put the classes that implement our |
49d823
|
91 |
resource objects, each of which also happens to be a domain model object. |
e53e13
|
92 |
|
CM |
93 |
Here is the source for ``models.py``: |
|
94 |
|
f5a9a5
|
95 |
.. literalinclude:: src/basiclayout/tutorial/models.py |
SP |
96 |
:linenos: |
|
97 |
:language: python |
e53e13
|
98 |
|
b0b299
|
99 |
#. *Lines 4-5*. The ``MyModel`` :term:`resource` class is implemented here. |
49d823
|
100 |
Instances of this class are capable of being persisted in :term:`ZODB` |
2a5ae0
|
101 |
because the class inherits from the |
CM |
102 |
:class:`persistent.mapping.PersistentMapping` class. The ``__parent__`` |
|
103 |
and ``__name__`` are important parts of the :term:`traversal` protocol. |
beb4f1
|
104 |
By default, set these to ``None`` to indicate that this is the |
2a5ae0
|
105 |
:term:`root` object. |
e53e13
|
106 |
|
b0b299
|
107 |
#. *Lines 8-14*. ``appmaker`` is used to return the *application |
e53e13
|
108 |
root* object. It is called on *every request* to the |
fd5ae9
|
109 |
:app:`Pyramid` application. It also performs bootstrapping by |
e53e13
|
110 |
*creating* an application root (inside the ZODB root object) if one |
34a913
|
111 |
does not already exist. It is used by the ``root_factory`` we've defined |
b25335
|
112 |
in our ``__init__.py``. |
e53e13
|
113 |
|
34a913
|
114 |
Bootstrapping is done by first seeing if the database has the persistent |
SP |
115 |
application root. If not, we make an instance, store it, and commit the |
|
116 |
transaction. We then return the application root object. |
e53e13
|
117 |
|
2a5ae0
|
118 |
Views With ``views.py`` |
CM |
119 |
----------------------- |
|
120 |
|
beb4f1
|
121 |
Our cookiecutter generated a default ``views.py`` on our behalf. It |
2a5ae0
|
122 |
contains a single view, which is used to render the page shown when you visit |
CM |
123 |
the URL ``http://localhost:6543/``. |
|
124 |
|
|
125 |
Here is the source for ``views.py``: |
|
126 |
|
f5a9a5
|
127 |
.. literalinclude:: src/basiclayout/tutorial/views.py |
SP |
128 |
:linenos: |
|
129 |
:language: python |
2a5ae0
|
130 |
|
CM |
131 |
Let's try to understand the components in this module: |
|
132 |
|
|
133 |
#. *Lines 1-2*. Perform some dependency imports. |
|
134 |
|
b0b299
|
135 |
#. *Line 5*. Use the :func:`pyramid.view.view_config` :term:`configuration |
2a5ae0
|
136 |
decoration` to perform a :term:`view configuration` registration. This |
CM |
137 |
view configuration registration will be activated when the application is |
|
138 |
started. It will be activated by virtue of it being found as the result |
0694f0
|
139 |
of a :term:`scan` (when Line 14 of ``__init__.py`` is run). |
2a5ae0
|
140 |
|
CM |
141 |
The ``@view_config`` decorator accepts a number of keyword arguments. We |
|
142 |
use two keyword arguments here: ``context`` and ``renderer``. |
|
143 |
|
|
144 |
The ``context`` argument signifies that the decorated view callable should |
|
145 |
only be run when :term:`traversal` finds the ``tutorial.models.MyModel`` |
|
146 |
:term:`resource` to be the :term:`context` of a request. In English, this |
|
147 |
means that when the URL ``/`` is visited, because ``MyModel`` is the root |
|
148 |
model, this view callable will be invoked. |
|
149 |
|
|
150 |
The ``renderer`` argument names an :term:`asset specification` of |
0694f0
|
151 |
``templates/mytemplate.pt``. This asset specification points at a |
CM |
152 |
:term:`Chameleon` template which lives in the ``mytemplate.pt`` file |
2a5ae0
|
153 |
within the ``templates`` directory of the ``tutorial`` package. And |
CM |
154 |
indeed if you look in the ``templates`` directory of this package, you'll |
|
155 |
see a ``mytemplate.pt`` template file, which renders the default home page |
0694f0
|
156 |
of the generated project. This asset specification is *relative* (to the |
49d823
|
157 |
view.py's current package). Alternatively we could have used the |
0694f0
|
158 |
absolute asset specification ``tutorial:templates/mytemplate.pt``, but |
CM |
159 |
chose to use the relative version. |
2a5ae0
|
160 |
|
CM |
161 |
Since this call to ``@view_config`` doesn't pass a ``name`` argument, the |
|
162 |
``my_view`` function which it decorates represents the "default" view |
|
163 |
callable used when the context is of the type ``MyModel``. |
|
164 |
|
b0b299
|
165 |
#. *Lines 6-7*. We define a :term:`view callable` named ``my_view``, which |
2a5ae0
|
166 |
we decorated in the step above. This view callable is a *function* we |
beb4f1
|
167 |
write generated by the ``zodb`` cookiecutter that is given a |
2a5ae0
|
168 |
``request`` and which returns a dictionary. The ``mytemplate.pt`` |
CM |
169 |
:term:`renderer` named by the asset specification in the step above will |
|
170 |
convert this dictionary to a :term:`response` on our behalf. |
|
171 |
|
|
172 |
The function returns the dictionary ``{'project':'tutorial'}``. This |
|
173 |
dictionary is used by the template named by the ``mytemplate.pt`` asset |
|
174 |
specification to fill in certain values on the page. |
|
175 |
|
b25335
|
176 |
Configuration in ``development.ini`` |
CM |
177 |
------------------------------------ |
2a5ae0
|
178 |
|
beb4f1
|
179 |
The ``development.ini`` (in the ``tutorial`` :term:`project` directory, as |
SP |
180 |
opposed to the ``tutorial`` :term:`package` directory) looks like this: |
2a5ae0
|
181 |
|
9a8a21
|
182 |
.. literalinclude:: src/basiclayout/development.ini |
f5a9a5
|
183 |
:language: ini |
2a5ae0
|
184 |
|
14ffa6
|
185 |
Note the existence of a ``[app:main]`` section which specifies our WSGI |
3d338e
|
186 |
application. Our ZODB database settings are specified as the |
CM |
187 |
``zodbconn.uri`` setting within this section. This value, and the other |
34a913
|
188 |
values within this section, are passed as ``**settings`` to the ``main`` |
2a5ae0
|
189 |
function we defined in ``__init__.py`` when the server is started via |
cfb2b5
|
190 |
``pserve``. |