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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
============
Basic Layout
============
The starter files generated by the ``alchemy`` scaffold are very basic, but
they provide a good orientation for the high-level patterns common to most
:term:`url dispatch` -based :app:`Pyramid` projects.
The source code for this tutorial stage can be browsed at
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/
<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/>`_.
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
it's contained within is a package, and to contain configuration code. Our
``__init__.py`` file will look like this:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:linenos:
:language: py
Let's go over this piece-by-piece. First, we need some imports to support
later code:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:end-before: main
:linenos:
:language: py
``__init__.py`` defines a function named ``main``. Here is the entirety of
the ``main`` function we've defined in our ``__init__.py``:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:pyobject: main
:linenos:
:language: py
When you invoke the ``pserve development.ini`` command, the ``main`` function
above is executed. It accepts some settings and returns a :term:`WSGI`
application. You can read :ref:`startup_chapter` for details about *how*
this function is found and called when you run ``pserve``, but for purposes
of brevity, we'll elide the details here.
The main function first creates a SQLAlchemy database engine using
``engine_from_config`` from the ``sqlalchemy.`` prefixed settings in the
``development.ini`` file's ``[app:main]`` section. This will be a URI
(something like ``sqlite://``):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 9
:linenos:
:language: py
``main`` then initializes our SQL database using SQLAlchemy, passing it the
engine:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 10
:language: py
The next step of ``main`` is to construct a :term:`Configurator` object:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 11
:language: py
``settings`` is passed to the Configurator as a keyword argument with the
dictionary values passed as the ``**settings`` argument. This will be a
dictionary of settings parsed from the ``.ini`` file, which contains
deployment-related values such as ``pyramid.reload_templates``,
``db_string``, etc.
``'main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with
two arguments: ``static`` (the name), and ``static`` (the path):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 12
:language: py
This registers a static resource view which will match any URL that starts
with the prefix ``/static`` (by virtue of the first argument to add_static
view). This will serve up static resources for us from within the ``static``
directory of our ``tutorial`` package, in this case, via
``http://localhost:6543/static/`` and below (by virtue of the second argument
to add_static_view). 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.
Using the configurator ``main`` also registers a :term:`route configuration`
via the :meth:`pyramid.config.Configurator.add_route` method that will be
used when the URL is ``/``:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 13
:language: py
Since this route has a ``pattern`` equalling ``/`` it is the route that will
be matched when the URL ``/`` is visted, e.g. ``http://localhost:6543/``.
``main`` next calls the ``scan`` method of the configurator, which will
recursively scan our ``tutorial`` package, looking for ``@view_config`` (and
other special) decorators. When it finds a ``@view_config`` decorator, a
view configuration will be registered, which will allow one of our
application URLs to be mapped to some code.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 14
:language: py
Finally, ``main`` is finished configuring things, so it uses the
:meth:`pyramid.config.Configurator.make_wsgi_app` method to return a
:term:`WSGI` application:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 15
:language: py
View Declarations via ``views.py``
----------------------------------
Mapping a :term:`route` to code that will be executed when that route's
pattern matches is done by registering a :term:`view configuration`. Our
application uses the :meth:`pyramid.view.view_config` decorator to map view
callables to each route, thereby mapping URL patterns to code.
Here is the code in the ``views.py`` file within our package:
.. literalinclude:: src/basiclayout/tutorial/views.py
:linenos:
:language: py
The important part to point out here is the ``@view_config`` decorator. In
fact, ``@view_config`` is so important that we're going to ignore the rest of
the code in the module at this point just to explain it. The
``@view_config`` decorator associates the function it decorates with a
:term:`view configuration`. The view configuration names a ``route_name``
(``home``), and names a ``renderer``, which is a template which lives in the
``templates`` subdirectory of the package.
As the result of this view configuration, when the pattern associated with
the view named ``home`` is matched during a request, the function named
``my_view`` will be executed. The the function named ``my_view`` returns a
dictionary; the renderer will use the ``templates/mytemplate.pt`` template to
create a response based on the values in the dictionary.
Note that the decorated function named ``my_view`` accepts a single argument
named ``request``. This is the standard call signature for a Pyramid
:term:`view callable`.
Remember in our ``__init__.py`` when we executed the
:meth:`pyramid.config.Configurator.scan` method, e.g. ``config.scan()``? The
purpose of calling the scan method was to find and process this
``@view_config`` decorator in order to create a view configuration within our
application. Without being processed by ``scan``, the decorator effectively
does nothing. ``@view_config`` is inert without being detected via a
:term:`scan`.
Content Models with ``models.py``
---------------------------------
In a SQLAlchemy-based application, a *model* object is an object composed by
querying the SQL database. SQLAlchemy is an "object relational mapper" (an
ORM). The ``models.py`` file is where the ``alchemy`` scaffold put the
classes that implement our models.
Let's take a look. First, we need some imports to support later code.
.. literalinclude:: src/basiclayout/tutorial/models.py
:end-before: DBSession
:linenos:
:language: py
Next we set up a SQLAlchemy "DBSession" object:
.. literalinclude:: src/basiclayout/tutorial/models.py
:lines: 15-16
:linenos:
:language: py
We also need to create a declarative ``Base`` object to use as a
base class for our model:
.. literalinclude:: src/basiclayout/tutorial/models.py
:lines: 17
:language: py
To give a simple example of a model class, we define one named ``MyModel``:
.. literalinclude:: src/basiclayout/tutorial/models.py
:pyobject: MyModel
:linenos:
:language: py
Our sample model has an ``__init__`` that takes a two arguments (``name``,
and ``value``). It stores these values as ``self.name`` and ``self.value``
within the ``__init__`` function itself. The ``MyModel`` class also has a
``__tablename__`` attribute. This informs SQLAlchemy which table to use to
store the data representing instances of this class.
Next we define a function named ``populate`` which adds a single
model instance into our SQL storage and commits a transaction:
.. literalinclude:: src/basiclayout/tutorial/models.py
:pyobject: populate
:linenos:
:language: py
The function doesn't do a lot in this case, but it's there to illustrate
how an application requiring many objects to be set up could work.
Lastly we have a function named ``initialize_sql`` which receives a SQL
database engine and binds it to our SQLAlchemy DBSession object. It also
calls the ``populate`` function, to do initial database population. This
is the initialization function that is called from __init__.py above.
.. literalinclude:: src/basiclayout/tutorial/models.py
:pyobject: initialize_sql
:linenos:
:language: py
Here is the complete source for ``models.py``:
.. literalinclude:: src/basiclayout/tutorial/models.py
:linenos:
:language: py
|