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
|
============
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 :mod:`repoze.bfg` projects.
The source code for this tutorial stage can be browsed at
`docs.repoze.org <http://docs.repoze.org/bfgwiki2-1.3/basiclayout>`_.
``__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``
--------------------------------------
:mod:`repoze.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, using the
``http://namespaces.repoze.org/bfg`` namespace.
#. *Line 4*. Boilerplate, the comment explains.
#. *Lines 6-11*. Register a ``<route>`` :term:`route configuration`
that will be used when the URL is ``/``. Since this ``<route>``
has an empty ``pattern`` attribute, it is the "default" route. The
attribute named ``view`` with the value ``.views.my_view`` is the
dotted name to a *function* we write (generated by the
``bfg_routesalchemy`` template) that is given a ``request`` object
and which returns a response or a dictionary. You will use mostly
``<route>`` statements in a :term:`URL dispatch` based application
to map URLs to code. This ``route`` also names a
``view_renderer``, which is a template which lives in the
``templates`` subdirectory of the package. When the
``.views.my_view`` view returns a dictionary, a :term:`renderer`
will use this template to create a response.
#. *Lines 13-16*. Register a ``<static>`` directive that will match
any URL that starts with ``/static/``. This will serve up static
resources for us, in this case, at
``http://localhost:6543/static/`` and below. With this
declaration, we're saying that any URL that starts with ``/static``
should go to the static view; any remainder of its path (e.g. the
``/foo`` in ``/static/foo``) will be used to compose a path to a
static file resource, such as a CSS file.
Content Models with ``models.py``
---------------------------------
In a SQLAlchemy-based application, a *model* object is an object
composed by querying the SQL database which backs an application.
SQLAlchemy is an "object relational mapper" (an ORM). The
``models.py`` file is where the ``bfg_routesalchemy`` 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 21*. Set up a SQLAlchemy metadata object.
#. *Lines 23-25*. A model class named ``Model``. It has an
``__init__`` that takes a single argument (``name``). It stores a
single attribute named ``name``.
#. *Lines 27-32*. A SQLAlchemy ``Table`` declaration named
``models_table`` which we'll use later to map onto our ``Model``
class.
#. *Line 34*. 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 36-41*. A function named ``populate`` which adds a single
model instance into our SQL storage and commits a transaction.
#. *Lines 43-51*. 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``
---------------------------
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``:
.. literalinclude:: src/basiclayout/tutorial/run.py
:linenos:
:language: py
#. *Lines 1-3*. Imports to support later code.
#. *Lines 11-14*. Get the database configuration string from the
``tutorial.ini`` file's ``[app:sql]`` section. This will be a URI
(something like ``sqlite://``).
#. Line *15*. We initialize our SQL database using SQLAlchemy, passing
it the db string.
#. *Line 16*. We construct a :term:`Configurator`. ``settings`` is
passed as a keyword argument with the dictionary values passed by
PasteDeploy as the ``settings`` argument. This will be a
dictionary of settings parsed by PasteDeploy, which contains
deployment-related values such as ``reload_templates``,
``db_string``, etc.
#. *Lines 17-20*. We then load a ZCML file to do application
configuration, and use the
:meth:`repoze.bfg.configuration.Configurator.make_wsgi_app` method
to return a :term:`WSGI` application.
|