.. _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 ` is one such library. In this step, we introduce Deform for our forms. This also gives us :ref:`Colander ` 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 in turn pulls in Colander as a dependency: .. literalinclude:: forms/setup.py :emphasize-lines: 6 :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/pytest tutorial/tests.py -q .. 6 passed in 0.81 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 ````. 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.