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
|
============
Basic Layout
============
The starter files generated by the ``zodb`` scaffold are very basic, but
they provide a good orientation for the high-level patterns common to most
:term:`traversal`-based (and :term:`ZODB`-based) :app:`Pyramid` projects.
Application configuration 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. We use ``__init__.py`` both as a marker, indicating the directory
in which it's contained is a package, and to contain application configuration
code.
When you run the application using the ``pserve`` command using the
``development.ini`` generated configuration file, the application
configuration points at a 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``. Let's take a look at the code and describe what
it does:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:linenos:
:language: py
#. *Lines 1-3*. Perform some dependency imports.
#. *Lines 6-8*. Define a :term:`root factory` for our Pyramid application.
#. *Line 11*. ``__init__.py`` defines a function named ``main``.
#. *Line 14*. We construct a :term:`Configurator` with a root
factory and the settings keywords parsed by :term:`PasteDeploy`. The root
factory is named ``root_factory``.
#. *Line 15*. Include support for the :term:`Chameleon` template rendering
bindings, allowing us to use the ``.pt`` templates.
#. *Line 16*. Register a "static view", which answers requests whose URL
paths start with ``/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 second argument of this tag is the "path",
which is a relative :term:`asset specification`, so it finds the resources
it should serve within the ``static`` directory inside the ``tutorial``
package. Alternatively the scaffold could have used an *absolute* asset
specification as the path (``tutorial:static``).
#. *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. We don't pass any arguments to
:meth:`~pyramid.config.Configurator.scan`, which implies that the scan
should take place in the current package (in this case, ``tutorial``).
The scaffold could have equivalently said ``config.scan('tutorial')``, but
it chose to omit the package name argument.
#. *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 ``zodb`` scaffold put the classes that implement our
resource objects, each of which also happens to be a domain model object.
Here is the source for ``models.py``:
.. literalinclude:: src/basiclayout/tutorial/models.py
:linenos:
:language: py
#. *Lines 4-5*. The ``MyModel`` :term:`resource` class is implemented here.
Instances of this class are 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 8-14*. ``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. It is used by the ``root_factory`` we've defined
in our ``__init__.py``.
Bootstrapping is done 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 scaffold 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 5*. 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 14 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
``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. This asset specification is *relative* (to the
view.py's current package). Alternatively we could have used the
absolute asset specification ``tutorial:templates/mytemplate.pt``, but
chose to use the relative version.
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 6-7*. 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 ``zodb`` scaffold 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.
Configuration 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/basiclayout/development.ini
:language: ini
Note the existence of a ``[app:main]`` section which specifies our WSGI
application. Our ZODB database settings are specified as the
``zodbconn.uri`` setting within this section. This value, and the other
values within this section, are passed as ``**settings`` to the ``main``
function we defined in ``__init__.py`` when the server is started via
``pserve``.
|