commit | author | age
|
da42d5
|
1 |
|
49d634
|
2 |
.. _quick_tour: |
PE |
3 |
|
ae9014
|
4 |
===================== |
PE |
5 |
Quick Tour of Pyramid |
|
6 |
===================== |
5b47ba
|
7 |
|
9b7f4d
|
8 |
Pyramid lets you start small and finish big. This *Quick Tour* of Pyramid is |
257ac0
|
9 |
for those who want to evaluate Pyramid, whether you are new to Python web |
SP |
10 |
frameworks, or a pro in a hurry. For more detailed treatment of each topic, |
|
11 |
give the :ref:`quick_tutorial` a try. |
|
12 |
|
e6581f
|
13 |
If you would prefer to cut and paste the example code in this tour you may browse the source code located in the `Pyramid repository in the directory "docs/quick_tour" <https://github.com/Pylons/pyramid/>`. If you have downloaded the source code, you will find the tour in the same location. |
9b7f4d
|
14 |
|
b731b5
|
15 |
Installation |
71b83e
|
16 |
============ |
83fefb
|
17 |
|
257ac0
|
18 |
Once you have a standard Python environment setup, getting started with Pyramid |
SP |
19 |
is a breeze. Unfortunately "standard" is not so simple in Python. For this |
9ce94f
|
20 |
Quick Tour, it means `Python <https://www.python.org/downloads/>`_, :mod:`python:venv` (or `virtualenv for |
SP |
21 |
Python 2.7 <https://virtualenv.pypa.io/en/stable/>`_), |
315e46
|
22 |
`pip <https://pypi.org/project/pip/>`_, and `Setuptools |
9ce94f
|
23 |
<https://pypi.org/project/setuptools/>`_. |
9b7f4d
|
24 |
|
ebbe68
|
25 |
To save a little bit of typing and to be certain that we use the modules, |
SP |
26 |
scripts, and packages installed in our virtual environment, we'll set an |
|
27 |
environment variable, too. |
|
28 |
|
1c44d2
|
29 |
As an example, for Python 3 on Linux: |
83fefb
|
30 |
|
ebca90
|
31 |
.. parsed-literal:: |
83fefb
|
32 |
|
ebbe68
|
33 |
# set an environment variable to where you want your virtual environment |
SP |
34 |
$ export VENV=~/env |
|
35 |
# create the virtual environment |
|
36 |
$ python3 -m venv $VENV |
|
37 |
# install pyramid |
|
38 |
$ $VENV/bin/pip install pyramid |
556258
|
39 |
# or for a specific released version |
ebbe68
|
40 |
$ $VENV/bin/pip install "pyramid==\ |release|\ " |
83fefb
|
41 |
|
b731b5
|
42 |
For Windows: |
9b7f4d
|
43 |
|
ebca90
|
44 |
.. parsed-literal:: |
9b7f4d
|
45 |
|
ebbe68
|
46 |
# set an environment variable to where you want your virtual environment |
108121
|
47 |
c:\\> set VENV=c:\\env |
ebbe68
|
48 |
# create the virtual environment |
b730ae
|
49 |
c:\\> python -m venv %VENV% |
ebbe68
|
50 |
# install pyramid |
SP |
51 |
c:\\> %VENV%\\Scripts\\pip install pyramid |
556258
|
52 |
# or for a specific released version |
ebbe68
|
53 |
c:\\> %VENV%\\Scripts\\pip install "pyramid==\ |release|\ " |
9b7f4d
|
54 |
|
bdd544
|
55 |
Of course Pyramid runs fine on Python 2.7+, as do the examples in this *Quick |
ebbe68
|
56 |
Tour*. We're showing Python 3 for simplicity. (Pyramid had production support |
SP |
57 |
for Python 3 in October 2011.) Also for simplicity, the remaining examples will |
5af300
|
58 |
show only Unix commands. |
bf84d9
|
59 |
|
2033ee
|
60 |
.. seealso:: See also: |
b731b5
|
61 |
:ref:`Quick Tutorial section on Requirements <qtut_requirements>`, |
21f2b6
|
62 |
:ref:`installing_unix`, :ref:`Before You Install <installing_chapter>`, |
SP |
63 |
:ref:`Why use $VENV/bin/pip instead of source bin/activate, then pip |
|
64 |
<venv-bin-pip-vs-source-bin-activate>`, and |
257ac0
|
65 |
:ref:`Installing Pyramid on a Windows System <installing_windows>`. |
SP |
66 |
|
71b83e
|
67 |
|
PE |
68 |
Hello World |
|
69 |
=========== |
47eaa1
|
70 |
|
257ac0
|
71 |
Microframeworks have shown that learning starts best from a very small first |
SP |
72 |
step. Here's a tiny application in Pyramid: |
47eaa1
|
73 |
|
ae9014
|
74 |
.. literalinclude:: quick_tour/hello_world/app.py |
71b83e
|
75 |
:linenos: |
da42d5
|
76 |
:language: python |
47eaa1
|
77 |
|
PE |
78 |
This simple example is easy to run. Save this as ``app.py`` and run it: |
|
79 |
|
|
80 |
.. code-block:: bash |
|
81 |
|
ebbe68
|
82 |
$ $VENV/bin/python ./app.py |
47eaa1
|
83 |
|
1273d0
|
84 |
Next open http://localhost:6543/ in a browser, and you will see the ``Hello |
SP |
85 |
World!`` message. |
47eaa1
|
86 |
|
58febc
|
87 |
New to Python web programming? If so, some lines in the module merit |
71b83e
|
88 |
explanation: |
47eaa1
|
89 |
|
4b84f6
|
90 |
#. *Lines 6-7*. Implement the view code that generates the :term:`response`. |
SP |
91 |
|
257ac0
|
92 |
#. *Line 10*. ``if __name__ == '__main__':`` is Python's way of saying "Start |
SP |
93 |
here when running from the command line". |
47eaa1
|
94 |
|
2d7b91
|
95 |
#. *Lines 11-13*. Use Pyramid's :term:`configurator` in a :term:`context manager` to connect :term:`view` |
257ac0
|
96 |
code to a particular URL :term:`route`. |
47eaa1
|
97 |
|
71b83e
|
98 |
#. *Lines 14-16*. Publish a :term:`WSGI` app using an HTTP server. |
47eaa1
|
99 |
|
257ac0
|
100 |
As shown in this example, the :term:`configurator` plays a central role in |
SP |
101 |
Pyramid development. Building an application from loosely-coupled parts via |
|
102 |
:doc:`../narr/configuration` is a central idea in Pyramid, one that we will |
|
103 |
revisit regurlarly in this *Quick Tour*. |
d559af
|
104 |
|
2033ee
|
105 |
.. seealso:: See also: |
b731b5
|
106 |
:ref:`Quick Tutorial Hello World <qtut_hello_world>`, |
257ac0
|
107 |
:ref:`firstapp_chapter`, and :ref:`Todo List Application in One File |
SP |
108 |
<cookbook:single-file-tutorial>`. |
|
109 |
|
5b47ba
|
110 |
|
1273d0
|
111 |
Handling web requests and responses |
d559af
|
112 |
=================================== |
47eaa1
|
113 |
|
257ac0
|
114 |
Developing for the web means processing web requests. As this is a critical |
SP |
115 |
part of a web application, web developers need a robust, mature set of software |
|
116 |
for web requests. |
47eaa1
|
117 |
|
257ac0
|
118 |
Pyramid has always fit nicely into the existing world of Python web development |
d5b5d0
|
119 |
(virtual environments, packaging, cookiecutters, one of the first to embrace |
257ac0
|
120 |
Python 3, etc.). Pyramid turned to the well-regarded :term:`WebOb` Python |
SP |
121 |
library for request and response handling. In our example above, Pyramid hands |
|
122 |
``hello_world`` a ``request`` that is :ref:`based on WebOb <webob_chapter>`. |
47eaa1
|
123 |
|
71b83e
|
124 |
Let's see some features of requests and responses in action: |
PE |
125 |
|
ae9014
|
126 |
.. literalinclude:: quick_tour/requests/app.py |
da42d5
|
127 |
:language: python |
71b83e
|
128 |
:pyobject: hello_world |
PE |
129 |
|
257ac0
|
130 |
In this Pyramid view, we get the URL being visited from ``request.url``. Also |
1273d0
|
131 |
if you visited http://localhost:6543/?name=alice in a browser, the name is |
ebbe68
|
132 |
included in the body of the response: |
SP |
133 |
|
|
134 |
.. code-block:: text |
71b83e
|
135 |
|
2cbda9
|
136 |
URL http://localhost:6543/?name=alice with name: alice |
d559af
|
137 |
|
257ac0
|
138 |
Finally we set the response's content type, and return the Response. |
5b47ba
|
139 |
|
2033ee
|
140 |
.. seealso:: See also: |
257ac0
|
141 |
:ref:`Quick Tutorial Request and Response <qtut_request_response>` and |
SP |
142 |
:ref:`webob_chapter`. |
|
143 |
|
71b83e
|
144 |
|
PE |
145 |
Views |
|
146 |
===== |
|
147 |
|
257ac0
|
148 |
For the examples above, the ``hello_world`` function is a "view". In Pyramid |
SP |
149 |
views are the primary way to accept web requests and return responses. |
71b83e
|
150 |
|
d559af
|
151 |
So far our examples place everything in one file: |
PE |
152 |
|
|
153 |
- the view function |
|
154 |
|
|
155 |
- its registration with the configurator |
|
156 |
|
1273d0
|
157 |
- the route to map it to an URL |
d559af
|
158 |
|
PE |
159 |
- the WSGI application launcher |
|
160 |
|
257ac0
|
161 |
Let's move the views out to their own ``views.py`` module and change the |
SP |
162 |
``app.py`` to scan that module, looking for decorators that set up the views. |
1273d0
|
163 |
|
257ac0
|
164 |
First our revised ``app.py``: |
71b83e
|
165 |
|
ae9014
|
166 |
.. literalinclude:: quick_tour/views/app.py |
da42d5
|
167 |
:language: python |
71b83e
|
168 |
:linenos: |
PE |
169 |
|
257ac0
|
170 |
We added some more routes, but we also removed the view code. Our views and |
SP |
171 |
their registrations (via decorators) are now in a module ``views.py``, which is |
|
172 |
scanned via ``config.scan('views')``. |
71b83e
|
173 |
|
257ac0
|
174 |
We now have a ``views.py`` module that is focused on handling requests and |
SP |
175 |
responses: |
71b83e
|
176 |
|
ae9014
|
177 |
.. literalinclude:: quick_tour/views/views.py |
da42d5
|
178 |
:language: python |
71b83e
|
179 |
:linenos: |
PE |
180 |
|
1273d0
|
181 |
We have four views, each leading to the other. If you start at |
257ac0
|
182 |
http://localhost:6543/, you get a response with a link to the next view. The |
SP |
183 |
``hello_view`` (available at the URL ``/howdy``) has a link to the |
|
184 |
``redirect_view``, which issues a redirect to the final view. |
65d171
|
185 |
|
257ac0
|
186 |
Earlier we saw ``config.add_view`` as one way to configure a view. This section |
SP |
187 |
introduces ``@view_config``. Pyramid's configuration supports :term:`imperative |
|
188 |
configuration`, such as the ``config.add_view`` in the previous example. You |
|
189 |
can also use :term:`declarative configuration` in which a Python |
|
190 |
:term:`decorator` is placed on the line above the view. Both approaches result |
|
191 |
in the same final configuration, thus usually it is simply a matter of taste. |
71b83e
|
192 |
|
2033ee
|
193 |
.. seealso:: See also: |
257ac0
|
194 |
:ref:`Quick Tutorial Views <qtut_views>`, :doc:`../narr/views`, |
SP |
195 |
:doc:`../narr/viewconfig`, and :ref:`debugging_view_configuration`. |
|
196 |
|
71b83e
|
197 |
|
d559af
|
198 |
Routing |
PE |
199 |
======= |
71b83e
|
200 |
|
257ac0
|
201 |
Writing web applications usually means sophisticated URL design. We just saw |
SP |
202 |
some Pyramid machinery for requests and views. Let's look at features that help |
|
203 |
with routing. |
47eaa1
|
204 |
|
d559af
|
205 |
Above we saw the basics of routing URLs to views in Pyramid: |
47eaa1
|
206 |
|
257ac0
|
207 |
- Your project's "setup" code registers a route name to be used when matching |
SP |
208 |
part of the URL. |
47eaa1
|
209 |
|
257ac0
|
210 |
- Elsewhere a view is configured to be called for that route name. |
47eaa1
|
211 |
|
d559af
|
212 |
.. note:: |
47eaa1
|
213 |
|
257ac0
|
214 |
Why do this twice? Other Python web frameworks let you create a route and |
SP |
215 |
associate it with a view in one step. As illustrated in |
|
216 |
:ref:`routes_need_ordering`, multiple routes might match the same URL |
|
217 |
pattern. Rather than provide ways to help guess, Pyramid lets you be |
|
218 |
explicit in ordering. Pyramid also gives facilities to avoid the problem. |
d559af
|
219 |
|
257ac0
|
220 |
What if we want part of the URL to be available as data in my view? We can use |
SP |
221 |
this route declaration, for example: |
47eaa1
|
222 |
|
ae9014
|
223 |
.. literalinclude:: quick_tour/routing/app.py |
da42d5
|
224 |
:language: python |
257ac0
|
225 |
:linenos: |
SP |
226 |
:lines: 6 |
|
227 |
:lineno-start: 6 |
47eaa1
|
228 |
|
257ac0
|
229 |
With this, URLs such as ``/howdy/amy/smith`` will assign ``amy`` to ``first`` |
SP |
230 |
and ``smith`` to ``last``. We can then use this data in our view: |
47eaa1
|
231 |
|
ae9014
|
232 |
.. literalinclude:: quick_tour/routing/views.py |
da42d5
|
233 |
:language: python |
257ac0
|
234 |
:linenos: |
SP |
235 |
:lines: 5-8 |
|
236 |
:lineno-start: 5 |
|
237 |
:emphasize-lines: 3 |
47eaa1
|
238 |
|
257ac0
|
239 |
``request.matchdict`` contains values from the URL that match the "replacement |
SP |
240 |
patterns" (the curly braces) in the route declaration. This information can |
|
241 |
then be used in your view. |
47eaa1
|
242 |
|
2033ee
|
243 |
.. seealso:: See also: |
257ac0
|
244 |
:ref:`Quick Tutorial Routing <qtut_routing>`, :doc:`../narr/urldispatch`, |
SP |
245 |
:ref:`debug_routematch_section`, and :doc:`../narr/router`. |
|
246 |
|
71b83e
|
247 |
|
d559af
|
248 |
Templating |
PE |
249 |
========== |
71b83e
|
250 |
|
257ac0
|
251 |
Ouch. We have been making our own ``Response`` and filling the response body |
SP |
252 |
with HTML. You usually won't embed an HTML string directly in Python, but |
|
253 |
instead you will use a templating language. |
47eaa1
|
254 |
|
257ac0
|
255 |
Pyramid doesn't mandate a particular database system, form library, and so on. |
SP |
256 |
It encourages replaceability. This applies equally to templating, which is |
|
257 |
fortunate: developers have strong views about template languages. That said, |
|
258 |
the Pylons Project officially supports bindings for Chameleon, Jinja2, and |
|
259 |
Mako. In this step let's use Chameleon. |
47eaa1
|
260 |
|
039d12
|
261 |
Let's add ``pyramid_chameleon``, a Pyramid :term:`add-on` which enables |
257ac0
|
262 |
Chameleon as a :term:`renderer` in our Pyramid application: |
561a7e
|
263 |
|
HH |
264 |
.. code-block:: bash |
|
265 |
|
2cbda9
|
266 |
$VENV/bin/pip install pyramid_chameleon |
561a7e
|
267 |
|
257ac0
|
268 |
With the package installed, we can include the template bindings into our |
SP |
269 |
configuration in ``app.py``: |
561a7e
|
270 |
|
257ac0
|
271 |
.. literalinclude:: quick_tour/templating/app.py |
da42d5
|
272 |
:language: python |
257ac0
|
273 |
:linenos: |
SP |
274 |
:lines: 6-8 |
|
275 |
:lineno-start: 6 |
|
276 |
:emphasize-lines: 2 |
561a7e
|
277 |
|
257ac0
|
278 |
Now lets change our ``views.py`` file: |
561a7e
|
279 |
|
ae9014
|
280 |
.. literalinclude:: quick_tour/templating/views.py |
da42d5
|
281 |
:language: python |
257ac0
|
282 |
:linenos: |
SP |
283 |
:emphasize-lines: 4,6 |
d559af
|
284 |
|
257ac0
|
285 |
Ahh, that looks better. We have a view that is focused on Python code. Our |
SP |
286 |
``@view_config`` decorator specifies a :term:`renderer` that points to our |
|
287 |
template file. Our view then simply returns data which is then supplied to our |
|
288 |
template ``hello_world.pt``: |
d559af
|
289 |
|
ae9014
|
290 |
.. literalinclude:: quick_tour/templating/hello_world.pt |
d559af
|
291 |
:language: html |
PE |
292 |
|
257ac0
|
293 |
Since our view returned ``dict(name=request.matchdict['name'])``, we can use |
SP |
294 |
``name`` as a variable in our template via ``${name}``. |
5b47ba
|
295 |
|
2033ee
|
296 |
.. seealso:: See also: |
b731b5
|
297 |
:ref:`Quick Tutorial Templating <qtut_templating>`, |
257ac0
|
298 |
:doc:`../narr/templates`, :ref:`debugging_templates`, and |
SP |
299 |
:ref:`available_template_system_bindings`. |
d559af
|
300 |
|
PE |
301 |
|
257ac0
|
302 |
Templating with Jinja2 |
SP |
303 |
====================== |
|
304 |
|
|
305 |
We just said Pyramid doesn't prefer one templating language over another. Time |
|
306 |
to prove it. Jinja2 is a popular templating system, modeled after Django's |
|
307 |
templates. Let's add ``pyramid_jinja2``, a Pyramid :term:`add-on` which enables |
|
308 |
Jinja2 as a :term:`renderer` in our Pyramid applications: |
47eaa1
|
309 |
|
PE |
310 |
.. code-block:: bash |
|
311 |
|
2cbda9
|
312 |
$VENV/bin/pip install pyramid_jinja2 |
47eaa1
|
313 |
|
257ac0
|
314 |
With the package installed, we can include the template bindings into our |
SP |
315 |
configuration: |
47eaa1
|
316 |
|
257ac0
|
317 |
.. literalinclude:: quick_tour/jinja2/app.py |
da42d5
|
318 |
:language: python |
257ac0
|
319 |
:linenos: |
SP |
320 |
:lines: 6-8 |
|
321 |
:lineno-start: 6 |
|
322 |
:emphasize-lines: 2 |
47eaa1
|
323 |
|
58febc
|
324 |
The only change in our view is to point the renderer at the ``.jinja2`` file: |
47eaa1
|
325 |
|
ae9014
|
326 |
.. literalinclude:: quick_tour/jinja2/views.py |
da42d5
|
327 |
:language: python |
257ac0
|
328 |
:linenos: |
SP |
329 |
:lines: 4-6 |
|
330 |
:lineno-start: 4 |
|
331 |
:emphasize-lines: 1 |
47eaa1
|
332 |
|
d559af
|
333 |
Our Jinja2 template is very similar to our previous template: |
47eaa1
|
334 |
|
ae9014
|
335 |
.. literalinclude:: quick_tour/jinja2/hello_world.jinja2 |
ebca90
|
336 |
:language: html |
47eaa1
|
337 |
|
d559af
|
338 |
Pyramid's templating add-ons register a new kind of renderer into your |
257ac0
|
339 |
application. The renderer registration maps to different kinds of filename |
SP |
340 |
extensions. In this case, changing the extension from ``.pt`` to ``.jinja2`` |
|
341 |
passed the view response through the ``pyramid_jinja2`` renderer. |
47eaa1
|
342 |
|
2033ee
|
343 |
.. seealso:: See also: |
257ac0
|
344 |
:ref:`Quick Tutorial Jinja2 <qtut_jinja2>`, `Jinja2 homepage |
SP |
345 |
<http://jinja.pocoo.org/>`_, and :ref:`pyramid_jinja2 Overview |
|
346 |
<jinja2:overview>`. |
|
347 |
|
47eaa1
|
348 |
|
1273d0
|
349 |
Static assets |
47eaa1
|
350 |
============= |
PE |
351 |
|
257ac0
|
352 |
Of course the Web is more than just markup. You need static assets: CSS, JS, |
SP |
353 |
and images. Let's point our web app at a directory from which Pyramid will |
|
354 |
serve some static assets. First let's make another call to the |
715ec5
|
355 |
:term:`configurator` in ``app.py``: |
47eaa1
|
356 |
|
ae9014
|
357 |
.. literalinclude:: quick_tour/static_assets/app.py |
715ec5
|
358 |
:language: python |
257ac0
|
359 |
:linenos: |
SP |
360 |
:lines: 6-8 |
|
361 |
:lineno-start: 6 |
|
362 |
:emphasize-lines: 2 |
47eaa1
|
363 |
|
PE |
364 |
This tells our WSGI application to map requests under |
257ac0
|
365 |
http://localhost:6543/static/ to files and directories inside a ``static`` |
SP |
366 |
directory alongside our Python module. |
47eaa1
|
367 |
|
1273d0
|
368 |
Next make a directory named ``static``, and place ``app.css`` inside: |
47eaa1
|
369 |
|
ae9014
|
370 |
.. literalinclude:: quick_tour/static_assets/static/app.css |
d559af
|
371 |
:language: css |
47eaa1
|
372 |
|
257ac0
|
373 |
All we need to do now is point to it in the ``<head>`` of our Jinja2 template, |
SP |
374 |
``hello_world.jinja2``: |
47eaa1
|
375 |
|
715ec5
|
376 |
.. literalinclude:: quick_tour/static_assets/hello_world_static.jinja2 |
257ac0
|
377 |
:language: jinja |
SP |
378 |
:linenos: |
|
379 |
:lines: 4-6 |
|
380 |
:lineno-start: 4 |
|
381 |
:emphasize-lines: 2 |
47eaa1
|
382 |
|
257ac0
|
383 |
This link presumes that our CSS is at a URL starting with ``/static/``. What if |
SP |
384 |
the site is later moved under ``/somesite/static/``? Or perhaps a web developer |
|
385 |
changes the arrangement on disk? Pyramid provides a helper to allow flexibility |
|
386 |
on URL generation: |
d559af
|
387 |
|
715ec5
|
388 |
.. literalinclude:: quick_tour/static_assets/hello_world.jinja2 |
257ac0
|
389 |
:language: jinja |
SP |
390 |
:linenos: |
|
391 |
:lines: 4-6 |
|
392 |
:lineno-start: 4 |
|
393 |
:emphasize-lines: 2 |
d559af
|
394 |
|
715ec5
|
395 |
By using ``request.static_url`` to generate the full URL to the static assets, |
SP |
396 |
you ensure that you stay in sync with the configuration and gain refactoring |
|
397 |
flexibility later. |
47eaa1
|
398 |
|
2033ee
|
399 |
.. seealso:: See also: |
b731b5
|
400 |
:ref:`Quick Tutorial Static Assets <qtut_static_assets>`, |
257ac0
|
401 |
:doc:`../narr/assets`, :ref:`preventing_http_caching`, and |
SP |
402 |
:ref:`influencing_http_caching`. |
|
403 |
|
5b47ba
|
404 |
|
47eaa1
|
405 |
Returning JSON |
PE |
406 |
============== |
|
407 |
|
257ac0
|
408 |
Modern web apps are more than rendered HTML. Dynamic pages now use JavaScript |
SP |
409 |
to update the UI in the browser by requesting server data as JSON. Pyramid |
|
410 |
supports this with a JSON renderer: |
47eaa1
|
411 |
|
ae9014
|
412 |
.. literalinclude:: quick_tour/json/views.py |
da42d5
|
413 |
:language: python |
257ac0
|
414 |
:linenos: |
SP |
415 |
:lines: 9- |
|
416 |
:lineno-start: 9 |
47eaa1
|
417 |
|
257ac0
|
418 |
This wires up a view that returns some data through the JSON :term:`renderer`, |
SP |
419 |
which calls Python's JSON support to serialize the data into JSON, and sets the |
|
420 |
appropriate HTTP headers. |
|
421 |
|
|
422 |
We also need to add a route to ``app.py`` so that our app will know how to |
|
423 |
respond to a request for ``hello.json``. |
|
424 |
|
|
425 |
.. literalinclude:: quick_tour/json/app.py |
da42d5
|
426 |
:language: python |
257ac0
|
427 |
:linenos: |
SP |
428 |
:lines: 6-8 |
|
429 |
:lineno-start: 6 |
|
430 |
:emphasize-lines: 2 |
5b47ba
|
431 |
|
2033ee
|
432 |
.. seealso:: See also: |
257ac0
|
433 |
:ref:`Quick Tutorial JSON <qtut_json>`, :ref:`views_which_use_a_renderer`, |
SP |
434 |
:ref:`json_renderer`, and :ref:`adding_and_overriding_renderers`. |
|
435 |
|
47eaa1
|
436 |
|
1273d0
|
437 |
View classes |
47eaa1
|
438 |
============ |
PE |
439 |
|
257ac0
|
440 |
So far our views have been simple, free-standing functions. Many times your |
SP |
441 |
views are related. They may have different ways to look at or work on the same |
|
442 |
data, or they may be a REST API that handles multiple operations. Grouping |
|
443 |
these together as a :ref:`view class <class_as_view>` makes sense and achieves |
|
444 |
the following goals. |
47eaa1
|
445 |
|
65d171
|
446 |
- Group views |
47eaa1
|
447 |
|
65d171
|
448 |
- Centralize some repetitive defaults |
47eaa1
|
449 |
|
65d171
|
450 |
- Share some state and helpers |
47eaa1
|
451 |
|
257ac0
|
452 |
The following shows a "Hello World" example with three operations: view a form, |
SP |
453 |
save a change, or press the delete button in our ``views.py``: |
47eaa1
|
454 |
|
ae9014
|
455 |
.. literalinclude:: quick_tour/view_classes/views.py |
da42d5
|
456 |
:language: python |
257ac0
|
457 |
:linenos: |
SP |
458 |
:lines: 7- |
|
459 |
:lineno-start: 7 |
47eaa1
|
460 |
|
257ac0
|
461 |
As you can see, the three views are logically grouped together. Specifically: |
47eaa1
|
462 |
|
257ac0
|
463 |
- The first view is returned when you go to ``/howdy/amy``. This URL is mapped |
SP |
464 |
to the ``hello`` route that we centrally set using the optional |
65d171
|
465 |
``@view_defaults``. |
47eaa1
|
466 |
|
65d171
|
467 |
- The second view is returned when the form data contains a field with |
257ac0
|
468 |
``form.edit``, such as clicking on ``<input type="submit" name="form.edit" |
SP |
469 |
value="Save">``. This rule is specified in the ``@view_config`` for that |
|
470 |
view. |
d559af
|
471 |
|
257ac0
|
472 |
- The third view is returned when clicking on a button such as ``<input |
SP |
473 |
type="submit" name="form.delete" value="Delete">``. |
d559af
|
474 |
|
257ac0
|
475 |
Only one route is needed, stated in one place atop the view class. Also, the |
SP |
476 |
assignment of ``name`` is done in the ``__init__`` function. Our templates can |
|
477 |
then use ``{{ view.name }}``. |
5b47ba
|
478 |
|
257ac0
|
479 |
Pyramid view classes, combined with built-in and custom predicates, have much |
SP |
480 |
more to offer: |
b731b5
|
481 |
|
PE |
482 |
- All the same view configuration parameters as function views |
|
483 |
|
257ac0
|
484 |
- One route leading to multiple views, based on information in the request or |
SP |
485 |
data such as ``request_param``, ``request_method``, ``accept``, ``header``, |
|
486 |
``xhr``, ``containment``, and ``custom_predicates`` |
b731b5
|
487 |
|
2033ee
|
488 |
.. seealso:: See also: |
257ac0
|
489 |
:ref:`Quick Tutorial View Classes <qtut_view_classes>`, :ref:`Quick |
SP |
490 |
Tutorial More View Classes <qtut_more_view_classes>`, and |
|
491 |
:ref:`class_as_view`. |
|
492 |
|
47eaa1
|
493 |
|
a0ebd7
|
494 |
Quick project startup with cookiecutters |
SP |
495 |
======================================== |
47eaa1
|
496 |
|
5cf8c3
|
497 |
So far we have done all of our *Quick Tour* as a single Python file. No Python |
SP |
498 |
packages, no structure. Most Pyramid projects, though, aren't developed this |
|
499 |
way. |
47eaa1
|
500 |
|
6b6d0e
|
501 |
To ease the process of getting started, the Pylons Project provides a :term:`cookiecutter` that generates sample Pyramid projects from project templates. This cookiecutter will install Pyramid and its dependencies as well. |
a0ebd7
|
502 |
|
SP |
503 |
First you'll need to install cookiecutter. |
47eaa1
|
504 |
|
PE |
505 |
.. code-block:: bash |
|
506 |
|
2cbda9
|
507 |
$VENV/bin/pip install cookiecutter |
47eaa1
|
508 |
|
28e688
|
509 |
Let's use the cookiecutter ``pyramid-cookiecutter-starter`` to create a starter Pyramid project in the current directory, entering values at the prompts as shown below for the following command. |
47eaa1
|
510 |
|
PE |
511 |
.. code-block:: bash |
|
512 |
|
0080e9
|
513 |
$VENV/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout 1.10-branch |
47eaa1
|
514 |
|
a0ebd7
|
515 |
If prompted for the first item, accept the default ``yes`` by hitting return. |
SP |
516 |
|
2cd381
|
517 |
.. code-block:: text |
SP |
518 |
|
28e688
|
519 |
You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before. |
2cd381
|
520 |
Is it okay to delete and re-clone it? [yes]: yes |
SP |
521 |
project_name [Pyramid Scaffold]: hello_world |
6ff6fa
|
522 |
repo_name [hello_world]: hello_world |
2cd381
|
523 |
Select template_language: |
SP |
524 |
1 - jinja2 |
|
525 |
2 - chameleon |
6204d8
|
526 |
3 - mako |
6b6d0e
|
527 |
Choose from 1, 2, 3 [1]: 1 |
SM |
528 |
Select backend: |
|
529 |
1 - none |
|
530 |
2 - sqlalchemy |
|
531 |
3 - zodb |
6204d8
|
532 |
Choose from 1, 2, 3 [1]: 1 |
a0ebd7
|
533 |
|
b488f7
|
534 |
We then run through the following commands. |
47eaa1
|
535 |
|
PE |
536 |
.. code-block:: bash |
|
537 |
|
a0ebd7
|
538 |
# Change directory into your newly created project. |
2cbda9
|
539 |
cd hello_world |
a0ebd7
|
540 |
# Create a new virtual environment... |
2cbda9
|
541 |
python3 -m venv env |
a0ebd7
|
542 |
# ...where we upgrade packaging tools... |
2cbda9
|
543 |
env/bin/pip install --upgrade pip setuptools |
a0ebd7
|
544 |
# ...and into which we install our project and its testing requirements. |
2cbda9
|
545 |
env/bin/pip install -e ".[testing]" |
2cd1ea
|
546 |
# Reset our environment variable for a new virtual environment. |
2cbda9
|
547 |
export VENV=~/hello_world/env |
47eaa1
|
548 |
|
5cf8c3
|
549 |
We are moving in the direction of a full-featured Pyramid project, with a |
SP |
550 |
proper setup for Python standards (packaging) and Pyramid configuration. This |
|
551 |
includes a new way of running your application: |
47eaa1
|
552 |
|
PE |
553 |
.. code-block:: bash |
|
554 |
|
2cbda9
|
555 |
$VENV/bin/pserve development.ini |
47eaa1
|
556 |
|
PE |
557 |
Let's look at ``pserve`` and configuration in more depth. |
5b47ba
|
558 |
|
2033ee
|
559 |
.. seealso:: See also: |
49cd00
|
560 |
:ref:`Quick Tutorial Cookiecutters <qtut_cookiecutters>`, |
b731b5
|
561 |
:ref:`project_narr`, and |
d5b5d0
|
562 |
:doc:`../narr/cookiecutters` |
47eaa1
|
563 |
|
1273d0
|
564 |
Application running with ``pserve`` |
47eaa1
|
565 |
=================================== |
PE |
566 |
|
2cd1ea
|
567 |
Prior to the cookiecutter, our project mixed a number of operational details into our |
5cf8c3
|
568 |
code. Why should my main code care which HTTP server I want and what port |
SP |
569 |
number to run on? |
d4bd29
|
570 |
|
5cf8c3
|
571 |
``pserve`` is Pyramid's application runner, separating operational details from |
SP |
572 |
your code. When you install Pyramid, a small command program called ``pserve`` |
|
573 |
is written to your ``bin`` directory. This program is an executable Python |
|
574 |
module. It's very small, getting most of its brains via import. |
47eaa1
|
575 |
|
5cf8c3
|
576 |
You can run ``pserve`` with ``--help`` to see some of its options. Doing so |
SP |
577 |
reveals that you can ask ``pserve`` to watch your development files and reload |
|
578 |
the server when they change: |
47eaa1
|
579 |
|
PE |
580 |
.. code-block:: bash |
|
581 |
|
2cbda9
|
582 |
$VENV/bin/pserve development.ini --reload |
47eaa1
|
583 |
|
5cf8c3
|
584 |
The ``pserve`` command has a number of other options and operations. Most of |
SP |
585 |
the work, though, comes from your project's wiring, as expressed in the |
|
586 |
configuration file you supply to ``pserve``. Let's take a look at this |
|
587 |
configuration file. |
49d634
|
588 |
|
2033ee
|
589 |
.. seealso:: See also: |
b731b5
|
590 |
:ref:`what_is_this_pserve_thing` |
47eaa1
|
591 |
|
1273d0
|
592 |
Configuration with ``.ini`` files |
47eaa1
|
593 |
================================= |
PE |
594 |
|
5cf8c3
|
595 |
Earlier in *Quick Tour* we first met Pyramid's configuration system. At that |
SP |
596 |
point we did all configuration in Python code. For example, the port number |
2cd1ea
|
597 |
chosen for our HTTP server was right there in Python code. Our cookiecutter has |
5cf8c3
|
598 |
moved this decision and more into the ``development.ini`` file: |
d4bd29
|
599 |
|
ae9014
|
600 |
.. literalinclude:: quick_tour/package/development.ini |
d4bd29
|
601 |
:language: ini |
47eaa1
|
602 |
|
5cf8c3
|
603 |
Let's take a quick high-level look. First the ``.ini`` file is divided into |
SP |
604 |
sections: |
47eaa1
|
605 |
|
5cf8c3
|
606 |
- ``[app:main]`` configures our WSGI app |
47eaa1
|
607 |
|
PE |
608 |
- ``[server:main]`` holds our WSGI server settings |
|
609 |
|
|
610 |
- Various sections afterwards configure our Python logging system |
|
611 |
|
d4bd29
|
612 |
We have a few decisions made for us in this configuration: |
47eaa1
|
613 |
|
1273d0
|
614 |
#. *WSGI app:* What package has our WSGI application in it? |
5cf8c3
|
615 |
``use = egg:hello_world`` in the app section tells the configuration what |
SP |
616 |
application to load. |
47eaa1
|
617 |
|
1273d0
|
618 |
#. *Easier development by automatic template reloading:* In development mode, |
SP |
619 |
you shouldn't have to restart the server when editing a Jinja2 template. |
5cf8c3
|
620 |
``pyramid.reload_templates = true`` sets this policy, which might be |
SP |
621 |
different in production. |
47eaa1
|
622 |
|
785776
|
623 |
#. *Choice of web server:* ``use = egg:waitress#main`` tells ``pserve`` to |
SP |
624 |
use the ``waitress`` server. |
|
625 |
|
6d120e
|
626 |
#. *Interfaces:* ``listen = localhost:6543`` tells ``waitress`` to listen on all interfaces on port 6543 for both IPv4 and IPv6. |
785776
|
627 |
|
d5b5d0
|
628 |
Additionally the ``development.ini`` generated by this cookiecutter wired up |
5cf8c3
|
629 |
Python's standard logging. We'll now see in the console, for example, a log on |
SP |
630 |
every request that comes in, as well as traceback information. |
47eaa1
|
631 |
|
2033ee
|
632 |
.. seealso:: See also: |
b731b5
|
633 |
:ref:`Quick Tutorial Application Configuration <qtut_ini>`, |
PE |
634 |
:ref:`environment_chapter` and |
|
635 |
:doc:`../narr/paste` |
49d634
|
636 |
|
5b47ba
|
637 |
|
1273d0
|
638 |
Easier development with ``debugtoolbar`` |
47eaa1
|
639 |
======================================== |
PE |
640 |
|
58febc
|
641 |
As we introduce the basics, we also want to show how to be productive in |
5cf8c3
|
642 |
development and debugging. For example, we just discussed template reloading |
SP |
643 |
and earlier we showed ``--reload`` for application reloading. |
47eaa1
|
644 |
|
5cf8c3
|
645 |
``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes several tools |
SP |
646 |
available in your browser. Adding it to your project illustrates several points |
|
647 |
about configuration. |
47eaa1
|
648 |
|
28e688
|
649 |
Our cookiecutter ``pyramid-cookiecutter-starter`` already configured our package to include the |
5cf8c3
|
650 |
add-on ``pyramid_debugtoolbar`` in its ``setup.py``: |
47eaa1
|
651 |
|
ae9014
|
652 |
.. literalinclude:: quick_tour/package/setup.py |
5cf8c3
|
653 |
:language: python |
af1e32
|
654 |
:lineno-match: |
65a6d3
|
655 |
:lines: 11-17 |
SP |
656 |
:emphasize-lines: 5 |
47eaa1
|
657 |
|
5cf8c3
|
658 |
It was installed when you previously ran: |
47eaa1
|
659 |
|
PE |
660 |
.. code-block:: bash |
|
661 |
|
2cbda9
|
662 |
$VENV/bin/pip install -e ".[testing]" |
47eaa1
|
663 |
|
5cf8c3
|
664 |
The ``pyramid_debugtoolbar`` package is a Pyramid add-on, which means we need |
65a6d3
|
665 |
to include its configuration into our web application. The cookiecutter already took care of this for us in its ``development.ini`` using the ``pyramid.includes`` facility: |
47eaa1
|
666 |
|
ae9014
|
667 |
.. literalinclude:: quick_tour/package/development.ini |
d4bd29
|
668 |
:language: ini |
af1e32
|
669 |
:lineno-match: |
SP |
670 |
:lines: 14-15 |
47eaa1
|
671 |
|
5cf8c3
|
672 |
You'll now see a Pyramid logo on the right side of your browser window, which |
SP |
673 |
when clicked opens a new window that provides introspective access to debugging |
|
674 |
information. Even better, if your web application generates an error, you will |
|
675 |
see a nice traceback on the screen. When you want to disable this toolbar, |
|
676 |
there's no need to change code: you can remove it from ``pyramid.includes`` in |
|
677 |
the relevant ``.ini`` configuration file. |
5b47ba
|
678 |
|
2033ee
|
679 |
.. seealso:: See also: |
5cf8c3
|
680 |
:ref:`Quick Tutorial pyramid_debugtoolbar <qtut_debugtoolbar>` and |
b731b5
|
681 |
:ref:`pyramid_debugtoolbar <toolbar:overview>` |
47eaa1
|
682 |
|
d363b4
|
683 |
Unit tests and ``pytest`` |
9c39f6
|
684 |
========================= |
47eaa1
|
685 |
|
5cf8c3
|
686 |
Yikes! We got this far and we haven't yet discussed tests. This is particularly |
SP |
687 |
egregious, as Pyramid has had a deep commitment to full test coverage since |
|
688 |
before its release. |
47eaa1
|
689 |
|
28e688
|
690 |
Our ``pyramid-cookiecutter-starter`` cookiecutter generated a ``tests.py`` module with |
948344
|
691 |
one unit test and one functional test in it. It also configured ``setup.py`` with test requirements: |
d363b4
|
692 |
``pytest`` as the test runner, ``WebTest`` for running view tests, and the |
65a6d3
|
693 |
``pytest-cov`` tool which yells at us for code that isn't tested: |
47eaa1
|
694 |
|
948344
|
695 |
.. literalinclude:: quick_tour/package/setup.py |
SP |
696 |
:language: python |
|
697 |
:lineno-match: |
65a6d3
|
698 |
:lines: 19-23 |
47eaa1
|
699 |
|
948344
|
700 |
.. literalinclude:: quick_tour/package/setup.py |
SP |
701 |
:language: python |
|
702 |
:lineno-match: |
65a6d3
|
703 |
:lines: 43-45 |
47eaa1
|
704 |
|
948344
|
705 |
We already installed the test requirements when we ran the command ``$VENV/bin/pip install -e ".[testing]"``. We can now run all our tests: |
47eaa1
|
706 |
|
PE |
707 |
.. code-block:: bash |
|
708 |
|
d363b4
|
709 |
$VENV/bin/pytest --cov --cov-report=term-missing |
47eaa1
|
710 |
|
ebbe68
|
711 |
This yields the following output. |
47eaa1
|
712 |
|
ebbe68
|
713 |
.. code-block:: text |
SP |
714 |
|
|
715 |
=========================== test session starts =========================== |
948344
|
716 |
platform darwin -- Python 3.6.0, pytest-3.0.5, py-1.4.32, pluggy-0.4.0 |
SP |
717 |
rootdir: /Users/stevepiercy/hello_world, inifile: pytest.ini |
|
718 |
plugins: cov-2.4.0 |
|
719 |
collected 2 items |
ebbe68
|
720 |
|
948344
|
721 |
hello_world/tests.py .. |
SP |
722 |
|
c8a5e0
|
723 |
------------- coverage: platform darwin, python 3.6.0-final-0 ------------- |
948344
|
724 |
Name Stmts Miss Cover Missing |
SP |
725 |
----------------------------------------------------------------------- |
|
726 |
hello_world/__init__.py 8 0 100% |
|
727 |
hello_world/views.py 3 0 100% |
|
728 |
----------------------------------------------------------------------- |
|
729 |
TOTAL 11 0 100% |
ebbe68
|
730 |
|
SP |
731 |
|
948344
|
732 |
========================= 2 passed in 1.37 seconds ========================= |
SP |
733 |
|
|
734 |
Our tests passed, and its coverage is complete. What did our test look like? |
47eaa1
|
735 |
|
ae9014
|
736 |
.. literalinclude:: quick_tour/package/hello_world/tests.py |
da42d5
|
737 |
:language: python |
5cf8c3
|
738 |
:linenos: |
47eaa1
|
739 |
|
5cf8c3
|
740 |
Pyramid supplies helpers for test writing, which we use in the test setup and |
948344
|
741 |
teardown. Our first test imports the view, makes a dummy request, and sees if the |
SP |
742 |
view returns what we expected. Our second test verifies that the response body from a request to the web root contains what we expected. |
47eaa1
|
743 |
|
2033ee
|
744 |
.. seealso:: See also: |
5cf8c3
|
745 |
:ref:`Quick Tutorial Unit Testing <qtut_unit_testing>`, :ref:`Quick |
SP |
746 |
Tutorial Functional Testing <qtut_functional_testing>`, and |
b731b5
|
747 |
:ref:`testing_chapter` |
5b47ba
|
748 |
|
83fefb
|
749 |
Logging |
PE |
750 |
======= |
|
751 |
|
5cf8c3
|
752 |
It's important to know what is going on inside our web application. In |
SP |
753 |
development we might need to collect some output. In production we might need |
|
754 |
to detect situations when other people use the site. We need *logging*. |
83fefb
|
755 |
|
c87565
|
756 |
Fortunately Pyramid uses the normal Python approach to logging. The ``development.ini`` file for your project has a number of lines that configure the |
5cf8c3
|
757 |
logging for you to some reasonable defaults. You then see messages sent by |
SP |
758 |
Pyramid (for example, when a new request comes in). |
83fefb
|
759 |
|
5cf8c3
|
760 |
Maybe you would like to log messages in your code? In your Python module, |
c87565
|
761 |
import and set up the logging in your ``views.py``: |
83fefb
|
762 |
|
8f30e8
|
763 |
.. literalinclude:: quick_tour/logging/hello_world/views/default.py |
5cf8c3
|
764 |
:language: python |
c87565
|
765 |
:lineno-match: |
5cf8c3
|
766 |
:lines: 3-4 |
83fefb
|
767 |
|
PE |
768 |
You can now, in your code, log messages: |
|
769 |
|
8f30e8
|
770 |
.. literalinclude:: quick_tour/logging/hello_world/views/default.py |
5cf8c3
|
771 |
:language: python |
c87565
|
772 |
:lineno-match: |
SP |
773 |
:lines: 7-8 |
5cf8c3
|
774 |
:emphasize-lines: 2 |
83fefb
|
775 |
|
c87565
|
776 |
This will log ``Some Message`` at a ``DEBUG`` log level to the |
5cf8c3
|
777 |
application-configured logger in your ``development.ini``. What controls that? |
SP |
778 |
These emphasized sections in the configuration file: |
83fefb
|
779 |
|
c87565
|
780 |
.. literalinclude:: quick_tour/logging/development.ini |
d4bd29
|
781 |
:language: ini |
c87565
|
782 |
:lineno-match: |
SP |
783 |
:lines: 34-50 |
5cf8c3
|
784 |
:emphasize-lines: 1-2,14-17 |
83fefb
|
785 |
|
5cf8c3
|
786 |
Our application, a package named ``hello_world``, is set up as a logger and |
SP |
787 |
configured to log messages at a ``DEBUG`` or higher level. When you visit |
556258
|
788 |
http://localhost:6543, your console will now show: |
SP |
789 |
|
|
790 |
.. code-block:: text |
d4bd29
|
791 |
|
c87565
|
792 |
2016-12-25 03:03:57,059 DEBUG [hello_world.views:8][waitress] Some Message |
5b47ba
|
793 |
|
2033ee
|
794 |
.. seealso:: See also: |
5cf8c3
|
795 |
:ref:`Quick Tutorial Logging <qtut_logging>` and :ref:`logging_chapter`. |
83fefb
|
796 |
|
PE |
797 |
Sessions |
|
798 |
======== |
|
799 |
|
5cf8c3
|
800 |
When people use your web application, they frequently perform a task that |
SP |
801 |
requires semi-permanent data to be saved. For example, a shopping cart. This is |
|
802 |
called a :term:`session`. |
83fefb
|
803 |
|
5cf8c3
|
804 |
Pyramid has basic built-in support for sessions. Third party packages such as |
SP |
805 |
``pyramid_redis_sessions`` provide richer session support. Or you can create |
|
806 |
your own custom sessioning engine. Let's take a look at the :doc:`built-in |
|
807 |
sessioning support <../narr/sessions>`. In our ``__init__.py`` we first import |
|
808 |
the kind of sessioning we want: |
83fefb
|
809 |
|
64009a
|
810 |
.. literalinclude:: quick_tour/sessions/hello_world/__init__.py |
5cf8c3
|
811 |
:language: python |
64009a
|
812 |
:lineno-match: |
SP |
813 |
:lines: 1-2 |
5cf8c3
|
814 |
:emphasize-lines: 2 |
83fefb
|
815 |
|
d4bd29
|
816 |
.. warning:: |
83fefb
|
817 |
|
5cf8c3
|
818 |
As noted in the session docs, this example implementation is not intended |
SP |
819 |
for use in settings with security implications. |
83fefb
|
820 |
|
d4bd29
|
821 |
Now make a "factory" and pass it to the :term:`configurator`'s |
PE |
822 |
``session_factory`` argument: |
83fefb
|
823 |
|
64009a
|
824 |
.. literalinclude:: quick_tour/sessions/hello_world/__init__.py |
5cf8c3
|
825 |
:language: python |
64009a
|
826 |
:lineno-match: |
8f30e8
|
827 |
:lines: 9-12 |
64009a
|
828 |
:emphasize-lines: 2-3 |
83fefb
|
829 |
|
5cf8c3
|
830 |
Pyramid's :term:`request` object now has a ``session`` attribute that we can |
SP |
831 |
use in our view code in ``views.py``: |
83fefb
|
832 |
|
8f30e8
|
833 |
.. literalinclude:: quick_tour/sessions/hello_world/views/default.py |
5cf8c3
|
834 |
:language: python |
64009a
|
835 |
:lineno-match: |
SP |
836 |
:lines: 7- |
5cf8c3
|
837 |
:emphasize-lines: 3-7 |
83fefb
|
838 |
|
64009a
|
839 |
We need to update our Jinja2 template ``templates/mytemplate.jinja2`` to show counter increment in the session: |
d4bd29
|
840 |
|
64009a
|
841 |
.. literalinclude:: quick_tour/sessions/hello_world/templates/mytemplate.jinja2 |
d4bd29
|
842 |
:language: jinja |
64009a
|
843 |
:lineno-match: |
SP |
844 |
:lines: 4-8 |
|
845 |
:emphasize-lines: 4 |
d4bd29
|
846 |
|
2033ee
|
847 |
.. seealso:: See also: |
5cf8c3
|
848 |
:ref:`Quick Tutorial Sessions <qtut_sessions>`, :ref:`sessions_chapter`, |
SP |
849 |
:ref:`flash_messages`, :ref:`session_module`, and |
|
850 |
:term:`pyramid_redis_sessions`. |
83fefb
|
851 |
|
PE |
852 |
|
|
853 |
Databases |
|
854 |
========= |
|
855 |
|
6a9366
|
856 |
Web applications mean data. Data means databases. Frequently SQL databases. SQL |
SP |
857 |
databases frequently mean an "ORM" (object-relational mapper.) In Python, ORM |
|
858 |
usually leads to the mega-quality *SQLAlchemy*, a Python package that greatly |
|
859 |
eases working with databases. |
83fefb
|
860 |
|
b488f7
|
861 |
Pyramid and SQLAlchemy are great friends. That friendship includes a cookiecutter! |
83fefb
|
862 |
|
PE |
863 |
.. code-block:: bash |
|
864 |
|
2cbda9
|
865 |
cd ~ |
0080e9
|
866 |
env/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout 1.10-branch |
b488f7
|
867 |
|
SP |
868 |
If prompted for the first item, accept the default ``yes`` by hitting return. |
|
869 |
|
2cd381
|
870 |
.. code-block:: text |
SP |
871 |
|
28e688
|
872 |
You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before. |
2cd381
|
873 |
Is it okay to delete and re-clone it? [yes]: yes |
SP |
874 |
project_name [Pyramid Scaffold]: sqla_demo |
6ff6fa
|
875 |
repo_name [sqla_demo]: sqla_demo |
6b6d0e
|
876 |
Select template_language: |
SM |
877 |
1 - jinja2 |
|
878 |
2 - chameleon |
|
879 |
3 - mako |
|
880 |
Choose from 1, 2, 3 [1]: 1 |
|
881 |
Select backend: |
|
882 |
1 - none |
|
883 |
2 - sqlalchemy |
|
884 |
3 - zodb |
|
885 |
Choose from 1, 2, 3 [1]: 2 |
b488f7
|
886 |
|
SP |
887 |
We then run through the following commands as before. |
|
888 |
|
|
889 |
.. code-block:: bash |
|
890 |
|
|
891 |
# Change directory into your newly created project. |
2cbda9
|
892 |
cd sqla_demo |
b488f7
|
893 |
# Create a new virtual environment... |
2cbda9
|
894 |
python3 -m venv env |
b488f7
|
895 |
# ...where we upgrade packaging tools... |
2cbda9
|
896 |
env/bin/pip install --upgrade pip setuptools |
b488f7
|
897 |
# ...and into which we install our project and its testing requirements. |
2cbda9
|
898 |
env/bin/pip install -e ".[testing]" |
b488f7
|
899 |
# Reset our environment variable for a new virtual environment. |
2cbda9
|
900 |
export VENV=~/sqla_demo/env |
83fefb
|
901 |
|
6a9366
|
902 |
We now have a working sample SQLAlchemy application with all dependencies |
SP |
903 |
installed. The sample project provides a console script to initialize a SQLite |
|
904 |
database with tables. Let's run it, then start the application: |
83fefb
|
905 |
|
PE |
906 |
.. code-block:: bash |
|
907 |
|
2cbda9
|
908 |
$VENV/bin/initialize_sqla_demo_db development.ini |
SP |
909 |
$VENV/bin/pserve development.ini |
83fefb
|
910 |
|
6a9366
|
911 |
The ORM eases the mapping of database structures into a programming language. |
d5b5d0
|
912 |
SQLAlchemy uses "models" for this mapping. The cookiecutter generated a sample |
6a9366
|
913 |
model: |
83fefb
|
914 |
|
414b67
|
915 |
.. literalinclude:: quick_tour/sqla_demo/sqla_demo/models/mymodel.py |
da42d5
|
916 |
:language: python |
b488f7
|
917 |
:lineno-match: |
SP |
918 |
:pyobject: MyModel |
83fefb
|
919 |
|
6a9366
|
920 |
View code, which mediates the logic between web requests and the rest of the |
SP |
921 |
system, can then easily get at the data thanks to SQLAlchemy: |
83fefb
|
922 |
|
3eb1c3
|
923 |
.. literalinclude:: quick_tour/sqla_demo/sqla_demo/views/default.py |
da42d5
|
924 |
:language: python |
b488f7
|
925 |
:lineno-match: |
SP |
926 |
:lines: 13 |
5b47ba
|
927 |
|
2033ee
|
928 |
.. seealso:: See also: |
6a9366
|
929 |
:ref:`Quick Tutorial Databases <qtut_databases>`, `SQLAlchemy |
SP |
930 |
<http://www.sqlalchemy.org/>`_, :ref:`making_a_console_script`, |
|
931 |
:ref:`bfg_sql_wiki_tutorial`, and :ref:`Application Transactions with |
|
932 |
pyramid_tm <tm:overview>`. |
|
933 |
|
47eaa1
|
934 |
|
83fefb
|
935 |
Forms |
PE |
936 |
===== |
47eaa1
|
937 |
|
6a9366
|
938 |
Developers have lots of opinions about web forms, thus there are many form |
SP |
939 |
libraries for Python. Pyramid doesn't directly bundle a form library, but |
|
940 |
*Deform* is a popular choice for forms, along with its related *Colander* |
|
941 |
schema system. |
47eaa1
|
942 |
|
6a9366
|
943 |
As an example, imagine we want a form that edits a wiki page. The form should |
SP |
944 |
have two fields on it, one of them a required title and the other a rich text |
|
945 |
editor for the body. With Deform we can express this as a Colander schema: |
83fefb
|
946 |
|
d4bd29
|
947 |
.. code-block:: python |
PE |
948 |
|
|
949 |
class WikiPage(colander.MappingSchema): |
|
950 |
title = colander.SchemaNode(colander.String()) |
|
951 |
body = colander.SchemaNode( |
|
952 |
colander.String(), |
|
953 |
widget=deform.widget.RichTextWidget() |
|
954 |
) |
|
955 |
|
6a9366
|
956 |
With this in place, we can render the HTML for a form, perhaps with form data |
SP |
957 |
from an existing page: |
d4bd29
|
958 |
|
PE |
959 |
.. code-block:: python |
|
960 |
|
|
961 |
form = self.wiki_form.render() |
|
962 |
|
|
963 |
We'd like to handle form submission, validation, and saving: |
|
964 |
|
|
965 |
.. code-block:: python |
|
966 |
|
|
967 |
# Get the form data that was posted |
|
968 |
controls = self.request.POST.items() |
|
969 |
try: |
|
970 |
# Validate and either raise a validation error |
|
971 |
# or return deserialized data from widgets |
|
972 |
appstruct = wiki_form.validate(controls) |
|
973 |
except deform.ValidationFailure as e: |
|
974 |
# Bail out and render form with errors |
|
975 |
return dict(title=title, page=page, form=e.render()) |
|
976 |
|
|
977 |
# Change the content and redirect to the view |
|
978 |
page['title'] = appstruct['title'] |
|
979 |
page['body'] = appstruct['body'] |
|
980 |
|
6a9366
|
981 |
Deform and Colander provide a very flexible combination for forms, widgets, |
SP |
982 |
schemas, and validation. Recent versions of Deform also include a :ref:`retail |
|
983 |
mode <deform:retail>` for gaining Deform features on custom forms. |
d4bd29
|
984 |
|
a0ad6f
|
985 |
Deform uses attractive CSS from Twitter Bootstrap and more powerful select, checkbox, and date and time widgets. |
d4bd29
|
986 |
|
2033ee
|
987 |
.. seealso:: See also: |
a0ad6f
|
988 |
:ref:`Quick Tutorial Forms <qtut_forms>`, :ref:`Deform <deform:overview>`, and :ref:`Colander <colander:overview>`. |
83fefb
|
989 |
|
d4bd29
|
990 |
Conclusion |
PE |
991 |
========== |
83fefb
|
992 |
|
ae9014
|
993 |
This *Quick Tour* covered a little about a lot. We introduced a long list |
PE |
994 |
of concepts in Pyramid, many of which are expanded on more fully in the |
540fcd
|
995 |
Pyramid developer docs. |