The source code for this tutorial stage can be browsed at docs.repoze.org.
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.
The bfg_zodb template uses ZCML to perform system configuration. The ZCML file generated by the template looks like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <configure xmlns="http://namespaces.repoze.org/bfg"> <!-- this must be included for the view declarations to work --> <include package="repoze.bfg.includes" /> <view context=".models.MyModel" view=".views.my_view" renderer="templates/mytemplate.pt" /> <static name="static" path="templates/static" /> </configure>
Line 1. The root <configure> element, in a bfg namespace.
Line 4. Boilerplate, the comment explains.
Lines 6-10. Register a <view> that names a context type that is a class. .views.my_view is a function we write (generated by the bfg_zodb template) that is given a context object and a request and which returns a dictionary. The renderer tag indicates that the templates/mytemplate.pt template should be used to turn the dictionary returned by the view into a response. templates/mytemplate.pt is a relative path: it names the mytemplate.pt file which lives in the templates subdirectory of the directory in which this configure.zcml lives in. In this case, it means it lives in the tutorial package’s templates directory as mytemplate.pt
Since this <view> doesn’t have a name attribute, it is the “default” view for that class.
Lines 12-15. Register a static view which answers requests which start with /static. This is a view that will serve up static resources for us, in this case, at http://localhost:6543/static/ and below. The path element of this tag is a relative directory name, so it finds the resources it should serve within the templates/static directory inside the tutorial package.
repoze.bfg often uses the word model when talking about content resources arranged in the hierarchical object graph consulted by traversal. The models.py file is where the bfg_zodb Paster template put the classes that implement our model objects.
Here is the source for models.py:
1 2 3 4 5 6 7 8 9 10 11 12 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 3-4. The MyModel class we referred to in the ZCML file named configure.zcml 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 6-12. appmaker is used to return the application root object. It is called on every request to the repoze.bfg 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.
When you run the application using the paster command using the tutorial.ini generated config file, the application configuration points at an Setuptools entry point described as egg:tutorial#app. In our application, because the application’s setup.py file says so, this entry point happens to be the app function within the file named run.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from repoze.bfg.configuration import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from tutorial.models import appmaker def app(global_config, **settings): """ This function returns a WSGI application. It is usually called by the PasteDeploy framework during ``paster serve``. """ zodb_uri = settings.get('zodb_uri') if zodb_uri is None: raise ValueError("No 'zodb_uri' in application configuration.") finder = PersistentApplicationFinder(zodb_uri, appmaker) def get_root(request): return finder(request.environ) config = Configurator(root_factory=get_root, settings=settings) config.begin() config.load_zcml('configure.zcml') config.end() return config.make_wsgi_app()