Permalink
Fetching contributors…
Cannot retrieve contributors at this time
191 lines (145 sloc) 9.01 KB

Basic Layout

The starter files generated by the zodb cookiecutter 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.

Open tutorial/__init__.py. It should already contain the following:

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

  1. Lines 1-3. Perform some dependency imports.
  2. Lines 6-8. Define a :term:`root factory` for our Pyramid application.
  3. Line 11. __init__.py defines a function named main.
  4. Line 14. Use an explicit transaction manager for apps so that they do not implicitly create new transactions when touching the manager outside of the pyramid_tm lifecycle.
  5. Line 15. Construct a :term:`Configurator` as a :term:`context manager` with the settings keyword parsed by :term:`PasteDeploy`.
  6. Line 16. Include support for the :term:`Chameleon` template rendering bindings, allowing us to use the .pt templates.
  7. Line 17. Include support for pyramid_tm, allowing Pyramid requests to join the active transaction as provided by the transaction package.
  8. Line 18. Include support for pyramid_retry to retry a request when transient exceptions occur.
  9. Line 19. Include support for pyramid_zodbconn, providing integration between :term:`ZODB` and a Pyramid application.
  10. Line 20. Set a root factory using our function named root_factory.
  11. Line 21. 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 cookiecutter could have used an absolute asset specification as the path (tutorial:static).
  12. Line 22. 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 cookiecutter could have equivalently said config.scan('tutorial'), but it chose to omit the package name argument.
  13. Line 23. 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 cookiecutter 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: python

  1. 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, set these to None to indicate that this is the :term:`root` object.

  2. Lines 8-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. 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 cookiecutter 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: python

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

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

  2. 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.

  3. 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 cookiecutter 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.