diff --git a/docs/conf.py b/docs/conf.py index 98db7c51..03e27217 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -242,5 +242,6 @@ latex_documents = [ intersphinx_mapping = { 'http://docs.python.org/dev': None, 'http://werkzeug.pocoo.org/documentation/dev/': None, - 'http://www.sqlalchemy.org/docs/': None + 'http://www.sqlalchemy.org/docs/': None, + 'http://wtforms.simplecodes.com/docs/0.5/': None } diff --git a/docs/installation.rst b/docs/installation.rst index a56ae3af..c5788be2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -96,14 +96,6 @@ This is possible as well, but I would not recommend it. Just run (Run it in an Admin shell on Windows systems and without the `sudo`). - -The Drop into Place Version ---------------------------- - -Now I really don't recommend this way on using Flask, but you can do that -of course as well. Download the `dip` zipfile from the website and unzip -it next to your application. - .. _windows-easy-install: `easy_install` on Windows diff --git a/docs/patterns/flashing.rst b/docs/patterns/flashing.rst index c54ef28f..fca9a9e1 100644 --- a/docs/patterns/flashing.rst +++ b/docs/patterns/flashing.rst @@ -1,3 +1,5 @@ +.. _message-flashing-pattern: + Message Flashing ================ diff --git a/docs/patterns/index.rst b/docs/patterns/index.rst index beba9cb8..037f6e11 100644 --- a/docs/patterns/index.rst +++ b/docs/patterns/index.rst @@ -16,5 +16,6 @@ end of the request, the database connection is closed again. packages sqlite3 sqlalchemy + wtforms templateinheritance flashing diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst new file mode 100644 index 00000000..e79f9d3a --- /dev/null +++ b/docs/patterns/wtforms.rst @@ -0,0 +1,109 @@ +Form Validation with WTForms +============================ + +When you have to work with form data submitted by a browser view code +quickly becomes very hard to read. There are libraries out there designed +to make this process easier to manage. One of them is WTForms which we +will handle here. If you find yourself in the situation of having many +forms, you might want to give it a try. + +When you are working with WTForms you have to define your forms as classes +first. I recommend breaking up the application into multiple modules +(:ref:`larger-applications`) for that and adding a separate module for the +forms. + +The Forms +--------- + +This is an example form for a typical registration page:: + + from wtforms import Form, BooleanField, TextField, validators + + class RegistrationForm(Form): + username = TextField('Username', [validators.Length(min=4, max=25)]) + email = TextField('Email Address', [validators.Length(min=6, max=35)]) + password = PasswordField('New Password', [Required(), + EqualTo('confirm', mesage='Passwords must match')]) + confirm = PasswordField('Repeat Password') + accept_tos = BooleanField('I accept the TOS', [validators.Required()]) + +In the View +----------- + +In the view function, the usage of this form looks like this:: + + @app.route('/register', methods=['GET', 'POST']) + def register(): + form = RegistrationForm(request.form) + if request.method == 'POST' and form.validate(): + user = User(form.username.data, form.email.data, + form.password.data) + db_session.add(user) + flash('Thanks for registering') + redirect(url_for('login')) + return render_template('register.html', form=form) + +Notice that we are implying that the view is using SQLAlchemy here +(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt +the code as necessary. + +Things to remember: + +1. create the form from the request :attr:`~flask.request.form` value if + the data is submitted via the HTTP `POST` method and + :attr:`~flask.request.args` if the data is submitted as `GET`. +2. to validate the data, call the :func:`~wtforms.form.Form.validate` + method which will return `True` if the data validates, `False` + otherwise. +3. to access individual values from the form, access `form..data`. + +Forms in Templates +------------------ + +Now to the template side. When you pass the form to the templates you can +easily render them there. Look at the following example template to see +how easy this is. WTForms does half the form generation for us already. +To make it even nicer, we can write a macro that renders a field with +label and a list of errors if there are any. + +Here an example `_formhelpers.html` template with such a macro: + +.. sourcecode:: html+jinja + + {% macro render_field(field) %} +
{{ field.label }} +
{{ field(**kwargs)|safe }} + {% if field.errors %} + + {% endif %} +
+ {% endmacro %} + +This macro accepts a couple of keyword arguments that are forwarded to +WTForm's field function that renders the field for us. They keyword +arguments will be inserted as HTML attributes. So for example you can +call ``render_field(form.username, class='username')`` to add a class to +the input element. Note that WTForms returns standard Python unicode +strings, so we have to tell Jinja2 that this data is already HTML escaped +with the `|safe` filter. + +Here the `register.html` template for the function we used above which +takes advantage of the `_formhelpers.html` template: + +.. sourcecode:: html+jinja + + {% from "_formhelpers.html" import render_field %} +
+
+ {{ render_field(form.username) }} + {{ render_field(form.email) }} + {{ render_field(form.password) }} + {{ render_field(form.confirm) }} + {{ render_field(form.accept_tos) }} +
+

+

diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 944b3955..3d3e765f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -368,7 +368,7 @@ u'Marked up \xbb HTML' .. [#] Unsure what that :class:`~flask.g` object is? It's something you can store information on yourself, check the documentation of that - object (:class:`~flask.g`) and the :ref:`database-pattern` for more + object (:class:`~flask.g`) and the :ref:`sqlite3` for more information.