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
|
============
Basic Layout
============
The starter files generated by the ``bfg_routesalchemy`` template are
basic, but they provide a good orientation for the high-level patterns
common to most :term:`url dispatch` -based BFG projects.
``__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.
Configuration With ``configure.zcml``
--------------------------------------
BFG uses a configuration markup language syntactically the same as
Zope's implementation of :term:`ZCML`, but using a different default
XML namespace. Our sample ZCML file looks like the following:
.. literalinclude:: src/basiclayout/tutorial/configure.zcml
:linenos:
:language: xml
#. *Line 1*. The root ``<configure>`` element, in a *BFG* namespace.
#. *Line 3*. Boilerplate, the comment explains.
#. *Lines 6-7*. Register a :term:`subscriber` that tears down the
SQLAlchemy connection after a request is finished.
#. *Lines 9-12*. Register a ``<route>`` that will be used when the
URL is ``/``. Since this ``<view>`` has a blank ``name``
attribute, it is the "default" view. ``.views.my_view`` refers to a
*function* we write (generated by the ``bfg_routesalchemy``
template) that is given a ``context`` and a ``request`` and returns
a response.
#. *Lines 14-17*. Register a ``<route>`` with a path that starts with
``/static``, capturing the rest of the URL as ``subpath``. This is
a view that will serve up static resources for us, in this case, at
``http://localhost:6543/static/`` and below.
Content Models with ``models.py``
---------------------------------
In the context of a SQLAlchemy-based application, a *model* object is
an object composed by quering the SQL database which backs an
application. SQLAlchemy is an "object relational mapper" (an ORM).
The ``models.py`` file is where the ``bfg_zodb`` Paster template put
the classes that implement our models.
Here is the source for ``models.py``:
.. literalinclude:: src/basiclayout/tutorial/models.py
:linenos:
:language: py
#. *Lines 1-16*. Imports to support later code.
#. *Line 18*. We set up a SQLAlchemy "DBSession" object here. We
specify that we'd like to use the "ZopeTransactionExtension". This
extension is an extension which allows us to use a *transaction
manager* instead of controlling commits and aborts to database
operations by hand.
#. *Line 20*. Set up a SQLAlchemy metadata object.
#. *Lines 22-24*. A model class named ``Model``. It has an
``__init__`` that takes a single argument (``name``). It stores a
single attribute named ``name``.
#. *Lines 26-31*. A SQLAlchemy ``Table`` declaration named
``models_table`` which we'll use later to map onto our ``Model``
class.
#. *Line 33*. We map our ``models_table`` table to our Models class
here. This makes an association between the ``Model`` class and
the ``models`` table in the database, as far as SQLAlchemy is
concerned.
#. *Lines 35-40*. A function named ``populate`` which adds a single
model instance into our SQL storage and commits a transaction.
#. *Lines 42-50*. A function named ``initialize_sql`` which sets up
an actual SQL database and binds it to our SQLAlchemy DBSession
object. It also calls the ``populate`` function, to do initial
database population.
App Startup with ``run.py``
---------------------------
How does a BFG application start up? When you run under ``paster``
using the ``tutorial.ini`` generated config file, the application area
points at an entry point. Our entry point happens to be in ``run.py``
and its ``app`` function:
.. literalinclude:: src/basiclayout/tutorial/run.py
:linenos:
:language: py
#. *Lines 1-5*. Imports to support later code.
#. *Lines 7-11*. We define a ``Cleanup`` class which has a
``__del__`` method (the method called at Python object
destruction), which calls a function.
#. *Lines 13-15*. An event :term:`subscriber` which adds a
``Cleanup`` instance to the WSGI environment as
``tutorial.sasession``. As a result of registering this event
subscriber, when the WSGI environment is cleaned up, our database
connection will be removed.
#. *Lines 17-24*. Get the database configuration string from the
``tutorial.ini`` file's ``[app:sql]`` section. This will be a URI
(something like ``sqlite://``).
#. Line *25*. We initialize our SQL database using SQLAlchemy, passing
it the db string.
#. Line *26*. We use the ``repoze.bfg.router.make_app`` to return a
:term:`WSGI` application. The ``make_app`` function's first
parameter is the "root factory". Since this is a URL dispatch
application, the root factory is ``None`` (we don't do any
:term:`traversal` in this app. The second argument is the
*package* representing our application, and the third argument,
``options`` is passed as a keyword argument. It contains a
dictionary of options parsed by PasteDeploy.
We'll later change ``run.py`` when we add :term:`authorization` to our
wiki application.
|