The starter files generated by the zodb scaffold are basic, but they provide a good orientation for the high-level patterns common to most traversal -based Pyramid (and ZODB based) projects.
The source code for this tutorial stage can be browsed via http://github.com/Pylons/pyramid/tree/1.3-branch/docs/tutorials/wiki/src/basiclayout/.
A directory on disk can be turned into a Python 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 pserve command using the development.ini generated config 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pyramid.config import Configurator from pyramid_zodbconn import get_connection from .models import appmaker def root_factory(request): conn = get_connection(request) return appmaker(conn.root()) def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(root_factory=root_factory, settings=settings) config.add_static_view('static', 'static', cache_max_age=3600) config.scan() return config.make_wsgi_app()
Pyramid uses the word resource to describe objects arranged hierarchically in a resource tree. This tree is consulted by traversal to map URLs to code. In this application, the resource tree represents the site structure, but it also represents the domain model of the application, because each resource is a node stored persistently in a ZODB database. The models.py file is where the zodb scaffold 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from persistent.mapping import PersistentMapping class MyModel(PersistentMapping): __parent__ = __name__ = None def appmaker(zodb_root): if not 'app_root' in zodb_root: app_root = MyModel() zodb_root['app_root'] = app_root import transaction transaction.commit() return zodb_root['app_root']
Lines 4-5. The MyModel resource class is implemented here. Instances of this class will be capable of being persisted in ZODB because the class inherits from the persistent.mapping.PersistentMapping class. The __parent__ and __name__ are important parts of the traversal protocol. By default, have these as None indicating that this is the root object.
Lines 8-14. appmaker is used to return the application root object. It is called on every request to the 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.
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.
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:
1 2 3 4 5 6 7 from pyramid.view import view_config from .models import MyModel @view_config(context=MyModel, renderer='templates/mytemplate.pt') def my_view(request): return {'project': 'tutorial'}
Let’s try to understand the components in this module:
Lines 1-2. Perform some dependency imports.
Line 5. Use the pyramid.view.view_config() configuration decoration to perform a 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 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 traversal finds the tutorial.models.MyModel resource to be the 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 asset specification of templates/mytemplate.pt. This asset specification points at a 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). We could have alternately an 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 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 renderer named by the asset specification in the step above will convert this dictionary to a 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 development.ini (in the tutorial project directory, as opposed to the tutorial package directory) looks like this:
###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
use = egg:tutorial
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar
pyramid_zodbconn
pyramid_tm
tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
keys = root, tutorial
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_tutorial]
level = DEBUG
handlers =
qualname = tutorial
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
Note the existence of an [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.