214 lines
18 KiB
HTML
214 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="en" data-content_root="../">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Form Validation with WTForms — Flask Documentation (3.2.x)</title>
|
||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=6625fa76" />
|
||
<link rel="stylesheet" type="text/css" href="../_static/flask.css?v=b87c8d14" />
|
||
<script src="../_static/documentation_options.js?v=56528222"></script>
|
||
<script src="../_static/doctools.js?v=9bcbadda"></script>
|
||
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
|
||
<script data-project="flask" data-version="3.2.x" src="../_static/describe_version.js?v=fa7f30d0"></script>
|
||
<link rel="icon" href="../_static/shortcut-icon.png"/>
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
<link rel="next" title="Template Inheritance" href="templateinheritance.html" />
|
||
<link rel="prev" title="View Decorators" href="viewdecorators.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="templateinheritance.html" title="Template Inheritance"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="viewdecorators.html" title="View Decorators"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Patterns for Flask</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Form Validation with WTForms</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section id="form-validation-with-wtforms">
|
||
<h1>Form Validation with WTForms<a class="headerlink" href="#form-validation-with-wtforms" title="Link to this heading">¶</a></h1>
|
||
<p>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 <a class="reference external" href="https://wtforms.readthedocs.io/">WTForms</a> which we
|
||
will handle here. If you find yourself in the situation of having many
|
||
forms, you might want to give it a try.</p>
|
||
<p>When you are working with WTForms you have to define your forms as classes
|
||
first. I recommend breaking up the application into multiple modules
|
||
(<a class="reference internal" href="packages.html"><span class="doc">Large Applications as Packages</span></a>) for that and adding a separate module for the
|
||
forms.</p>
|
||
<div class="admonition-getting-the-most-out-of-wtforms-with-an-extension admonition">
|
||
<p class="admonition-title">Getting the most out of WTForms with an Extension</p>
|
||
<p>The <a class="reference external" href="https://flask-wtf.readthedocs.io/">Flask-WTF</a> extension expands on this pattern and adds a
|
||
few little helpers that make working with forms and Flask more
|
||
fun. You can get it from <a class="reference external" href="https://pypi.org/project/Flask-WTF/">PyPI</a>.</p>
|
||
</div>
|
||
<section id="the-forms">
|
||
<h2>The Forms<a class="headerlink" href="#the-forms" title="Link to this heading">¶</a></h2>
|
||
<p>This is an example form for a typical registration page:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">wtforms</span><span class="w"> </span><span class="kn">import</span> <span class="n">Form</span><span class="p">,</span> <span class="n">BooleanField</span><span class="p">,</span> <span class="n">StringField</span><span class="p">,</span> <span class="n">PasswordField</span><span class="p">,</span> <span class="n">validators</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">RegistrationForm</span><span class="p">(</span><span class="n">Form</span><span class="p">):</span>
|
||
<span class="n">username</span> <span class="o">=</span> <span class="n">StringField</span><span class="p">(</span><span class="s1">'Username'</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">Length</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">25</span><span class="p">)])</span>
|
||
<span class="n">email</span> <span class="o">=</span> <span class="n">StringField</span><span class="p">(</span><span class="s1">'Email Address'</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">Length</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">35</span><span class="p">)])</span>
|
||
<span class="n">password</span> <span class="o">=</span> <span class="n">PasswordField</span><span class="p">(</span><span class="s1">'New Password'</span><span class="p">,</span> <span class="p">[</span>
|
||
<span class="n">validators</span><span class="o">.</span><span class="n">DataRequired</span><span class="p">(),</span>
|
||
<span class="n">validators</span><span class="o">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s1">'confirm'</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s1">'Passwords must match'</span><span class="p">)</span>
|
||
<span class="p">])</span>
|
||
<span class="n">confirm</span> <span class="o">=</span> <span class="n">PasswordField</span><span class="p">(</span><span class="s1">'Repeat Password'</span><span class="p">)</span>
|
||
<span class="n">accept_tos</span> <span class="o">=</span> <span class="n">BooleanField</span><span class="p">(</span><span class="s1">'I accept the TOS'</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">DataRequired</span><span class="p">()])</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="in-the-view">
|
||
<h2>In the View<a class="headerlink" href="#in-the-view" title="Link to this heading">¶</a></h2>
|
||
<p>In the view function, the usage of this form looks like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/register'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'POST'</span><span class="p">])</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">register</span><span class="p">():</span>
|
||
<span class="n">form</span> <span class="o">=</span> <span class="n">RegistrationForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span> <span class="ow">and</span> <span class="n">form</span><span class="o">.</span><span class="n">validate</span><span class="p">():</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="n">form</span><span class="o">.</span><span class="n">username</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">form</span><span class="o">.</span><span class="n">email</span><span class="o">.</span><span class="n">data</span><span class="p">,</span>
|
||
<span class="n">form</span><span class="o">.</span><span class="n">password</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
|
||
<span class="n">db_session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="n">flash</span><span class="p">(</span><span class="s1">'Thanks for registering'</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s1">'login'</span><span class="p">))</span>
|
||
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">'register.html'</span><span class="p">,</span> <span class="n">form</span><span class="o">=</span><span class="n">form</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Notice we’re implying that the view is using SQLAlchemy here
|
||
(<a class="reference internal" href="sqlalchemy.html"><span class="doc">SQLAlchemy in Flask</span></a>), but that’s not a requirement, of course. Adapt
|
||
the code as necessary.</p>
|
||
<p>Things to remember:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>create the form from the request <code class="xref py py-attr docutils literal notranslate"><span class="pre">form</span></code> value if
|
||
the data is submitted via the HTTP <code class="docutils literal notranslate"><span class="pre">POST</span></code> method and
|
||
<code class="xref py py-attr docutils literal notranslate"><span class="pre">args</span></code> if the data is submitted as <code class="docutils literal notranslate"><span class="pre">GET</span></code>.</p></li>
|
||
<li><p>to validate the data, call the <code class="xref py py-func docutils literal notranslate"><span class="pre">validate()</span></code>
|
||
method, which will return <code class="docutils literal notranslate"><span class="pre">True</span></code> if the data validates, <code class="docutils literal notranslate"><span class="pre">False</span></code>
|
||
otherwise.</p></li>
|
||
<li><p>to access individual values from the form, access <code class="code docutils literal notranslate"><span class="pre">form.<NAME>.data</span></code>.</p></li>
|
||
</ol>
|
||
</section>
|
||
<section id="forms-in-templates">
|
||
<h2>Forms in Templates<a class="headerlink" href="#forms-in-templates" title="Link to this heading">¶</a></h2>
|
||
<p>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.</p>
|
||
<p>Here’s an example <code class="file docutils literal notranslate"><span class="pre">_formhelpers.html</span></code> template with such a macro:</p>
|
||
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="cp">{%</span> <span class="k">macro</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">field</span><span class="o">)</span> <span class="cp">%}</span>
|
||
<span class="p"><</span><span class="nt">dt</span><span class="p">></span><span class="cp">{{</span> <span class="nv">field.label</span> <span class="cp">}}</span>
|
||
<span class="p"><</span><span class="nt">dd</span><span class="p">></span><span class="cp">{{</span> <span class="nv">field</span><span class="o">(**</span><span class="nv">kwargs</span><span class="o">)|</span><span class="nf">safe</span> <span class="cp">}}</span>
|
||
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">field.errors</span> <span class="cp">%}</span>
|
||
<span class="p"><</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">errors</span><span class="p">></span>
|
||
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">error</span> <span class="k">in</span> <span class="nv">field.errors</span> <span class="cp">%}</span>
|
||
<span class="p"><</span><span class="nt">li</span><span class="p">></span><span class="cp">{{</span> <span class="nv">error</span> <span class="cp">}}</span><span class="p"></</span><span class="nt">li</span><span class="p">></span>
|
||
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
|
||
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
|
||
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
|
||
<span class="p"></</span><span class="nt">dd</span><span class="p">></span>
|
||
<span class="cp">{%</span> <span class="k">endmacro</span> <span class="cp">%}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This macro accepts a couple of keyword arguments that are forwarded to
|
||
WTForm’s field function, which renders the field for us. The keyword
|
||
arguments will be inserted as HTML attributes. So, for example, you can
|
||
call <code class="docutils literal notranslate"><span class="pre">render_field(form.username,</span> <span class="pre">class='username')</span></code> to add a class to
|
||
the input element. Note that WTForms returns standard Python strings,
|
||
so we have to tell Jinja2 that this data is already HTML-escaped with
|
||
the <code class="docutils literal notranslate"><span class="pre">|safe</span></code> filter.</p>
|
||
<p>Here is the <code class="file docutils literal notranslate"><span class="pre">register.html</span></code> template for the function we used above, which
|
||
takes advantage of the <code class="file docutils literal notranslate"><span class="pre">_formhelpers.html</span></code> template:</p>
|
||
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="cp">{%</span> <span class="k">from</span> <span class="s2">"_formhelpers.html"</span> <span class="k">import</span> <span class="nv">render_field</span> <span class="cp">%}</span>
|
||
<span class="p"><</span><span class="nt">form</span> <span class="na">method</span><span class="o">=</span><span class="s">post</span><span class="p">></span>
|
||
<span class="p"><</span><span class="nt">dl</span><span class="p">></span>
|
||
<span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.username</span><span class="o">)</span> <span class="cp">}}</span>
|
||
<span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.email</span><span class="o">)</span> <span class="cp">}}</span>
|
||
<span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.password</span><span class="o">)</span> <span class="cp">}}</span>
|
||
<span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.confirm</span><span class="o">)</span> <span class="cp">}}</span>
|
||
<span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.accept_tos</span><span class="o">)</span> <span class="cp">}}</span>
|
||
<span class="p"></</span><span class="nt">dl</span><span class="p">></span>
|
||
<span class="p"><</span><span class="nt">p</span><span class="p">><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">submit</span> <span class="na">value</span><span class="o">=</span><span class="s">Register</span><span class="p">></span>
|
||
<span class="p"></</span><span class="nt">form</span><span class="p">></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>For more information about WTForms, head over to the <a class="reference external" href="https://wtforms.readthedocs.io/">WTForms
|
||
website</a>.</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<span id="sidebar-top"></span>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||
<div class="sphinxsidebarwrapper">
|
||
|
||
|
||
<p class="logo"><a href="../index.html">
|
||
<img class="logo" src="../_static/flask-vertical.png" alt="Logo of Flask"/>
|
||
</a></p>
|
||
|
||
|
||
<h3>Contents</h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">Form Validation with WTForms</a><ul>
|
||
<li><a class="reference internal" href="#the-forms">The Forms</a></li>
|
||
<li><a class="reference internal" href="#in-the-view">In the View</a></li>
|
||
<li><a class="reference internal" href="#forms-in-templates">Forms in Templates</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li><a href="../index.html">Overview</a>
|
||
<ul>
|
||
<li><a href="index.html">Patterns for Flask</a>
|
||
<ul>
|
||
<li>Previous: <a href="viewdecorators.html" title="previous chapter">View Decorators</a>
|
||
<li>Next: <a href="templateinheritance.html" title="next chapter">Template Inheritance</a></ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<search id="searchbox" style="display: none" role="search">
|
||
<h3 id="searchlabel">Quick search</h3>
|
||
<div class="searchformwrapper">
|
||
<form class="search" action="../search.html" method="get">
|
||
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</search>
|
||
<script>document.getElementById('searchbox').style.display = "block"</script><div id="ethical-ad-placement"></div>
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2010 Pallets.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
|
||
</div>
|
||
</body>
|
||
</html>
|