Steve Piercy
2017-10-22 4567204570eff25408278fd01919c1b048b9f7f1
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
.. _qtut_forms:
 
====================================
18: Forms and Validation with Deform
====================================
 
Schema-driven, autogenerated forms with validation.
 
 
Background
==========
 
Modern web applications deal extensively with forms. Developers, though, have a
wide range of philosophies about how frameworks should help them with their
forms. As such, Pyramid doesn't directly bundle one particular form library.
Instead there are a variety of form libraries that are easy to use in Pyramid.
 
:ref:`Deform <deform:overview>` is one such library. In this step, we introduce
Deform for our forms. This also gives us :ref:`Colander <colander:overview>`
for schemas and validation.
 
 
Objectives
==========
 
- Make a schema using Colander, the companion to Deform.
 
- Create a form with Deform and change our views to handle validation.
 
 
Steps
=====
 
#. First we copy the results of the ``view_classes`` step:
 
   .. code-block:: bash
 
    $ cd ..; cp -r view_classes forms; cd forms
 
#. Let's edit ``forms/setup.py`` to declare a dependency on Deform (which then
   pulls in Colander as a dependency:
 
   .. literalinclude:: forms/setup.py
    :emphasize-lines: 7
    :linenos:
 
#. We can now install our project in development mode:
 
   .. code-block:: bash
 
      $ $VENV/bin/pip install -e .
 
#. Register a static view in ``forms/tutorial/__init__.py`` for Deform's CSS,
   JavaScript, etc., as well as our demo wiki page's views:
 
   .. literalinclude:: forms/tutorial/__init__.py
    :linenos:
 
#. Implement the new views, as well as the form schemas and some dummy data, in
   ``forms/tutorial/views.py``:
 
   .. literalinclude:: forms/tutorial/views.py
    :linenos:
 
#. A template for the top of the "wiki" in ``forms/tutorial/wiki_view.pt``:
 
   .. literalinclude:: forms/tutorial/wiki_view.pt
    :language: html
    :linenos:
 
#. Another template for adding/editing in
   ``forms/tutorial/wikipage_addedit.pt``:
 
   .. literalinclude:: forms/tutorial/wikipage_addedit.pt
    :language: html
    :linenos:
 
#. Add a template at ``forms/tutorial/wikipage_view.pt`` for viewing a wiki
   page:
 
   .. literalinclude:: forms/tutorial/wikipage_view.pt
    :language: html
    :linenos:
 
#. Our tests in ``forms/tutorial/tests.py`` don't run, so let's modify them:
 
   .. literalinclude:: forms/tutorial/tests.py
    :linenos:
 
#. Run the tests:
 
   .. code-block:: bash
 
    $ $VENV/bin/py.test tutorial/tests.py -q
    ..
    2 passed in 0.45 seconds
 
#. Run your Pyramid application with:
 
   .. code-block:: bash
 
    $ $VENV/bin/pserve development.ini --reload
 
#. Open http://localhost:6543/ in a browser.
 
 
Analysis
========
 
This step helps illustrate the utility of asset specifications for static
assets. We have an outside package called Deform with static assets which need
to be published. We don't have to know where on disk it is located. We point at
the package, then the path inside the package.
 
We just need to include a call to ``add_static_view`` to make that directory
available at a URL. For Pyramid-specific packages, Pyramid provides a facility
(``config.include()``) which even makes that unnecessary for consumers of a
package. (Deform is not specific to Pyramid.)
 
Our forms have rich widgets which need the static CSS and JavaScript just
mentioned. Deform has a :term:`resource registry` which allows widgets to
specify which JavaScript and CSS are needed. Our ``wikipage_addedit.pt``
template shows how we iterated over that data to generate markup that includes
the needed resources.
 
Our add and edit views use a pattern called *self-posting forms*. Meaning, the
same URL is used to ``GET`` the form as is used to ``POST`` the form. The
route, the view, and the template are the same URL whether you are walking up
to it for the first time or you clicked a button.
 
Inside the view we do ``if 'submit' in self.request.params:`` to see if this
form was a ``POST`` where the user clicked on a particular button
``<input name="submit">``.
 
The form controller then follows a typical pattern:
 
- If you are doing a ``GET``, skip over and just return the form.
 
- If you are doing a ``POST``, validate the form contents.
 
- If the form is invalid, bail out by re-rendering the form with the supplied
  ``POST`` data.
 
- If the validation succeeded, perform some action and issue a redirect via
  ``HTTPFound``.
 
We are, in essence, writing our own form controller. Other Pyramid-based
systems, including ``pyramid_deform``, provide a form-centric view class which
automates much of this branching and routing.
 
 
Extra credit
============
 
#. Give a try at a button that goes to a delete view for a particular wiki
   page.