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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
============
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.
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 in
which it's contained is a package, and to contain application configuration
code.
Open ``tutorial/tutorial/__init__.py``. It should already contain the
following:
.. 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. (See :ref:`startup_chapter` for more about ``pserve``.)
The next step of ``main`` is to construct a :term:`Configurator` object:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 7
: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``,
``sqlalchemy.url``, and so on.
Next include :term:`Jinja2` templating bindings so that we can use renderers
with the ``.jinja2`` extension within our project.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 8
:language: py
Next include the module ``meta`` from the package ``models`` using a dotted
Python path.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 9
:language: py
``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: 10
: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: 11
:language: py
Since this route has a ``pattern`` equaling ``/``, it is the route that will
be matched when the URL ``/`` is visited, e.g., ``http://localhost:6543/``.
``main`` next calls the ``scan`` method of the configurator
(:meth:`pyramid.config.Configurator.scan`), 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: 12
: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: 13
:language: py
View declarations via the ``views`` package
-------------------------------------------
The main function of a web framework is mapping each URL pattern to code (a
:term:`view callable`) that is executed when the requested URL matches the
corresponding :term:`route`. Our application uses the
:meth:`pyramid.view.view_config` decorator to perform this mapping.
Open ``tutorial/tutorial/views/default.py`` in the ``views`` package. It
should already contain the following:
.. literalinclude:: src/basiclayout/tutorial/views/default.py
:linenos:
:language: py
The important part here is that the ``@view_config`` decorator associates the
function it decorates (``my_view``) with a :term:`view configuration`,
consisting of:
* a ``route_name`` (``home``)
* a ``renderer``, which is a template from the ``templates`` subdirectory of
the package.
When the pattern associated with the ``home`` view is matched during a request,
``my_view()`` will be executed. ``my_view()`` returns a dictionary; the
renderer will use the ``templates/mytemplate.jinja2`` template to create a
response based on the values in the dictionary.
Note that ``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 ``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`.
The sample ``my_view()`` created by the scaffold uses a ``try:`` and
``except:`` clause to detect if there is a problem accessing the project
database and provide an alternate error response. That response will include
the text shown at the end of the file, which will be displayed in the browser
to inform the user about possible actions to take to solve the problem.
Content models with the ``models`` package
------------------------------------------
In a SQLAlchemy-based application, a *model* object is an object composed by
querying the SQL database. The ``models`` package is where the ``alchemy``
scaffold put the classes that implement our models.
First, open ``tutorial/tutorial/models/__init__.py``, which should already
contain the following:
.. literalinclude:: src/basiclayout/tutorial/models/__init__.py
:linenos:
:language: py
Our ``__init__.py`` will perform some imports to support later code, then calls
the function :func:`sqlalchemy.orm.configure_mappers`.
Next open ``tutorial/tutorial/models/meta.py``, which should already contain
the following:
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:linenos:
:language: py
``meta.py`` contains imports that are used to support later code. We create a
dictionary ``NAMING_CONVENTION`` as well.
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:end-before: metadata
:linenos:
:language: py
Next we create a ``metadata`` object from the class
:class:`sqlalchemy.schema.MetaData`, using ``NAMING_CONVENTION`` as the value
for the ``naming_convention`` argument. We also need to create a declarative
``Base`` object to use as a base class for our model. Then our model classes
will inherit from the ``Base`` class so they can be associated with our
particular database connection.
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:lines: 15-16
:lineno-start: 15
:linenos:
:language: py
Next we define several functions, the first of which is ``includeme``, which
configures various database settings by calling subsequently defined functions.
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:pyobject: includeme
:linenos:
:language: py
The function ``get_session`` registers a database session with a transaction
manager, and returns a ``dbsession`` object. With the transaction manager, our
application will automatically issue a transaction commit after every request
unless an exception is raised, in which case the transaction will be aborted.
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:pyobject: get_session
:linenos:
:language: py
The ``get_engine`` function creates an :term:`SQLAlchemy` database engine using
:func:`sqlalchemy.engine_from_config` from the ``sqlalchemy.``-prefixed
settings in the ``development.ini`` file's ``[app:main]`` section, which is a
URI, something like ``sqlite://``.
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:pyobject: get_engine
:linenos:
:language: py
The function ``get_dbmaker`` accepts an :term:`SQLAlchemy` database engine,
and creates a database session object ``dbmaker`` from the :term:`SQLAlchemy`
class :class:`sqlalchemy.orm.session.sessionmaker`, which is then used for
creating a session with the database engine.
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
:pyobject: get_dbmaker
:linenos:
:language: py
To give a simple example of a model class, we define one named ``MyModel``:
.. literalinclude:: src/basiclayout/tutorial/models/mymodel.py
:pyobject: MyModel
:linenos:
:language: py
Our example model does not require an ``__init__`` method because SQLAlchemy
supplies for us a default constructor if one is not already present, which
accepts keyword arguments of the same name as that of the mapped attributes.
.. note:: Example usage of MyModel:
.. code-block:: python
johnny = MyModel(name="John Doe", value=10)
The ``MyModel`` class has a ``__tablename__`` attribute. This informs
SQLAlchemy which table to use to store the data representing instances of this
class.
The Index import and the Index object creation is not required for this
tutorial, and will be removed in the next step.
That's about all there is to it regarding models, views, and initialization
code in our stock application.
|