Resuelvo conflictos en la documentación

Signed-off-by: Edgar Alvarado Taleno <edgar.alvaradotaleno@ucr.ac.cr>
This commit is contained in:
Edgar Alvarado Taleno 2025-04-10 21:08:48 -06:00
commit 93eb72a5db
102 changed files with 33015 additions and 810 deletions

View file

@ -0,0 +1,249 @@
<!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>Application Setup &#8212; 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="Define and Access the Database" href="database.html" />
<link rel="prev" title="Project Layout" href="layout.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="database.html" title="Define and Access the Database"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="layout.html" title="Project Layout"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Application Setup</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="application-setup">
<h1>Application Setup<a class="headerlink" href="#application-setup" title="Link to this heading"></a></h1>
<p>A Flask application is an instance of the <a class="reference internal" href="../api.html#flask.Flask" title="flask.Flask"><code class="xref py py-class docutils literal notranslate"><span class="pre">Flask</span></code></a> class.
Everything about the application, such as configuration and URLs, will
be registered with this class.</p>
<p>The most straightforward way to create a Flask application is to create
a global <a class="reference internal" href="../api.html#flask.Flask" title="flask.Flask"><code class="xref py py-class docutils literal notranslate"><span class="pre">Flask</span></code></a> instance directly at the top of your code, like
how the “Hello, World!” example did on the previous page. While this is
simple and useful in some cases, it can cause some tricky issues as the
project grows.</p>
<p>Instead of creating a <a class="reference internal" href="../api.html#flask.Flask" title="flask.Flask"><code class="xref py py-class docutils literal notranslate"><span class="pre">Flask</span></code></a> instance globally, you will create
it inside a function. This function is known as the <em>application
factory</em>. Any configuration, registration, and other setup the
application needs will happen inside the function, then the application
will be returned.</p>
<section id="the-application-factory">
<h2>The Application Factory<a class="headerlink" href="#the-application-factory" title="Link to this heading"></a></h2>
<p>Its time to start coding! Create the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code> directory and add the
<code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> file. The <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> serves double duty: it will
contain the application factory, and it tells Python that the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code>
directory should be treated as a package.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ mkdir flaskr
</pre></div>
</div>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/__init__.py</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flask</span><span class="w"> </span><span class="kn">import</span> <span class="n">Flask</span>
<span class="k">def</span><span class="w"> </span><span class="nf">create_app</span><span class="p">(</span><span class="n">test_config</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="c1"># create and configure the app</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">instance_relative_config</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_mapping</span><span class="p">(</span>
<span class="n">SECRET_KEY</span><span class="o">=</span><span class="s1">&#39;dev&#39;</span><span class="p">,</span>
<span class="n">DATABASE</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">instance_path</span><span class="p">,</span> <span class="s1">&#39;flaskr.sqlite&#39;</span><span class="p">),</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">test_config</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># load the instance config, if it exists, when not testing</span>
<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s1">&#39;config.py&#39;</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># load the test config if passed in</span>
<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_mapping</span><span class="p">(</span><span class="n">test_config</span><span class="p">)</span>
<span class="c1"># ensure the instance folder exists</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">instance_path</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">OSError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="c1"># a simple page that says hello</span>
<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/hello&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">hello</span><span class="p">():</span>
<span class="k">return</span> <span class="s1">&#39;Hello, World!&#39;</span>
<span class="k">return</span> <span class="n">app</span>
</pre></div>
</div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">create_app</span></code> is the application factory function. Youll add to it
later in the tutorial, but it already does a lot.</p>
<ol class="arabic simple">
<li><p><code class="docutils literal notranslate"><span class="pre">app</span> <span class="pre">=</span> <span class="pre">Flask(__name__,</span> <span class="pre">instance_relative_config=True)</span></code> creates the
<a class="reference internal" href="../api.html#flask.Flask" title="flask.Flask"><code class="xref py py-class docutils literal notranslate"><span class="pre">Flask</span></code></a> instance.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">__name__</span></code> is the name of the current Python module. The app
needs to know where its located to set up some paths, and
<code class="docutils literal notranslate"><span class="pre">__name__</span></code> is a convenient way to tell it that.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">instance_relative_config=True</span></code> tells the app that
configuration files are relative to the
<a class="reference internal" href="../config.html#instance-folders"><span class="std std-ref">instance folder</span></a>. The instance folder
is located outside the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code> package and can hold local
data that shouldnt be committed to version control, such as
configuration secrets and the database file.</p></li>
</ul>
</li>
<li><p><a class="reference internal" href="../api.html#flask.Config.from_mapping" title="flask.Config.from_mapping"><code class="xref py py-meth docutils literal notranslate"><span class="pre">app.config.from_mapping()</span></code></a> sets
some default configuration that the app will use:</p>
<ul class="simple">
<li><p><a class="reference internal" href="../config.html#SECRET_KEY" title="SECRET_KEY"><code class="xref py py-data docutils literal notranslate"><span class="pre">SECRET_KEY</span></code></a> is used by Flask and extensions to keep data
safe. Its set to <code class="docutils literal notranslate"><span class="pre">'dev'</span></code> to provide a convenient value
during development, but it should be overridden with a random
value when deploying.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">DATABASE</span></code> is the path where the SQLite database file will be
saved. Its under
<a class="reference internal" href="../api.html#flask.Flask.instance_path" title="flask.Flask.instance_path"><code class="xref py py-attr docutils literal notranslate"><span class="pre">app.instance_path</span></code></a>, which is the
path that Flask has chosen for the instance folder. Youll learn
more about the database in the next section.</p></li>
</ul>
</li>
<li><p><a class="reference internal" href="../api.html#flask.Config.from_pyfile" title="flask.Config.from_pyfile"><code class="xref py py-meth docutils literal notranslate"><span class="pre">app.config.from_pyfile()</span></code></a> overrides
the default configuration with values taken from the <code class="docutils literal notranslate"><span class="pre">config.py</span></code>
file in the instance folder if it exists. For example, when
deploying, this can be used to set a real <code class="docutils literal notranslate"><span class="pre">SECRET_KEY</span></code>.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">test_config</span></code> can also be passed to the factory, and will be
used instead of the instance configuration. This is so the tests
youll write later in the tutorial can be configured
independently of any development values you have configured.</p></li>
</ul>
</li>
<li><p><a class="reference external" href="https://docs.python.org/3/library/os.html#os.makedirs" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">os.makedirs()</span></code></a> ensures that
<a class="reference internal" href="../api.html#flask.Flask.instance_path" title="flask.Flask.instance_path"><code class="xref py py-attr docutils literal notranslate"><span class="pre">app.instance_path</span></code></a> exists. Flask
doesnt create the instance folder automatically, but it needs to be
created because your project will create the SQLite database file
there.</p></li>
<li><p><a class="reference internal" href="../api.html#flask.Flask.route" title="flask.Flask.route"><code class="xref py py-meth docutils literal notranslate"><span class="pre">&#64;app.route()</span></code></a> creates a simple route so you can
see the application working before getting into the rest of the
tutorial. It creates a connection between the URL <code class="docutils literal notranslate"><span class="pre">/hello</span></code> and a
function that returns a response, the string <code class="docutils literal notranslate"><span class="pre">'Hello,</span> <span class="pre">World!'</span></code> in
this case.</p></li>
</ol>
</section>
<section id="run-the-application">
<h2>Run The Application<a class="headerlink" href="#run-the-application" title="Link to this heading"></a></h2>
<p>Now you can run your application using the <code class="docutils literal notranslate"><span class="pre">flask</span></code> command. From the
terminal, tell Flask where to find your application, then run it in
debug mode. Remember, you should still be in the top-level
<code class="docutils literal notranslate"><span class="pre">flask-tutorial</span></code> directory, not the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code> package.</p>
<p>Debug mode shows an interactive debugger whenever a page raises an
exception, and restarts the server whenever you make changes to the
code. You can leave it running and just reload the browser page as you
follow the tutorial.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ flask --app flaskr run --debug
</pre></div>
</div>
<p>Youll see output similar to this:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>* Serving Flask app &quot;flaskr&quot;
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: nnn-nnn-nnn
</pre></div>
</div>
<p>Visit <a class="reference external" href="http://127.0.0.1:5000/hello">http://127.0.0.1:5000/hello</a> in a browser and you should see the
“Hello, World!” message. Congratulations, youre now running your Flask
web application!</p>
<p>If another program is already using port 5000, youll see
<code class="docutils literal notranslate"><span class="pre">OSError:</span> <span class="pre">[Errno</span> <span class="pre">98]</span></code> or <code class="docutils literal notranslate"><span class="pre">OSError:</span> <span class="pre">[WinError</span> <span class="pre">10013]</span></code> when the
server tries to start. See <a class="reference internal" href="../server.html#address-already-in-use"><span class="std std-ref">Address already in use</span></a> for how to
handle that.</p>
<p>Continue to <a class="reference internal" href="database.html"><span class="doc">Define and Access the Database</span></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="#">Application Setup</a><ul>
<li><a class="reference internal" href="#the-application-factory">The Application Factory</a></li>
<li><a class="reference internal" href="#run-the-application">Run The Application</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="layout.html" title="previous chapter">Project Layout</a>
<li>Next: <a href="database.html" title="next chapter">Define and Access the Database</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,134 @@
<!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>Tutorial &#8212; 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="Project Layout" href="layout.html" />
<link rel="prev" title="Quickstart" href="../quickstart.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="layout.html" title="Project Layout"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="../quickstart.html" title="Quickstart"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Tutorial</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="tutorial">
<h1>Tutorial<a class="headerlink" href="#tutorial" title="Link to this heading"></a></h1>
<div class="toctree-wrapper compound">
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="layout.html">Project Layout</a></li>
<li class="toctree-l1"><a class="reference internal" href="factory.html">Application Setup</a></li>
<li class="toctree-l1"><a class="reference internal" href="database.html">Define and Access the Database</a></li>
<li class="toctree-l1"><a class="reference internal" href="views.html">Blueprints and Views</a></li>
<li class="toctree-l1"><a class="reference internal" href="templates.html">Templates</a></li>
<li class="toctree-l1"><a class="reference internal" href="static.html">Static Files</a></li>
<li class="toctree-l1"><a class="reference internal" href="blog.html">Blog Blueprint</a></li>
<li class="toctree-l1"><a class="reference internal" href="install.html">Make the Project Installable</a></li>
<li class="toctree-l1"><a class="reference internal" href="tests.html">Test Coverage</a></li>
<li class="toctree-l1"><a class="reference internal" href="deploy.html">Deploy to Production</a></li>
<li class="toctree-l1"><a class="reference internal" href="next.html">Keep Developing!</a></li>
</ul>
</div>
<p>This tutorial will walk you through creating a basic blog application
called Flaskr. Users will be able to register, log in, create posts,
and edit or delete their own posts. You will be able to package and
install the application on other computers.</p>
<img alt="screenshot of index page" class="screenshot align-center" src="../_images/flaskr_index.png" />
<p>Its assumed that youre already familiar with Python. The <a class="reference external" href="https://docs.python.org/3/tutorial/">official
tutorial</a> in the Python docs is a great way to learn or review first.</p>
<p>While its designed to give a good starting point, the tutorial doesnt
cover all of Flasks features. Check out the <a class="reference internal" href="../quickstart.html"><span class="doc">Quickstart</span></a> for an
overview of what Flask can do, then dive into the docs to find out more.
The tutorial only uses whats provided by Flask and Python. In another
project, you might decide to use <a class="reference internal" href="../extensions.html"><span class="doc">Extensions</span></a> or other libraries
to make some tasks simpler.</p>
<img alt="screenshot of login page" class="screenshot align-center" src="../_images/flaskr_login.png" />
<p>Flask is flexible. It doesnt require you to use any particular project
or code layout. However, when first starting, its helpful to use a more
structured approach. This means that the tutorial will require a bit of
boilerplate up front, but its done to avoid many common pitfalls that
new developers encounter, and it creates a project thats easy to expand
on. Once you become more comfortable with Flask, you can step out of
this structure and take full advantage of Flasks flexibility.</p>
<img alt="screenshot of edit page" class="screenshot align-center" src="../_images/flaskr_edit.png" />
<p><a class="reference external" href="https://github.com/pallets/flask/tree/main/examples/tutorial">The tutorial project is available as an example in the Flask
repository</a>, if you want to compare your project
with the final product as you follow the tutorial.</p>
<p>Continue to <a class="reference internal" href="layout.html"><span class="doc">Project Layout</span></a>.</p>
</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>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li>Previous: <a href="../quickstart.html" title="previous chapter">Quickstart</a>
<li>Next: <a href="layout.html" title="next chapter">Project Layout</a>
</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,178 @@
<!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>Make the Project Installable &#8212; 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="Test Coverage" href="tests.html" />
<link rel="prev" title="Blog Blueprint" href="blog.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="tests.html" title="Test Coverage"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="blog.html" title="Blog Blueprint"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Make the Project Installable</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="make-the-project-installable">
<h1>Make the Project Installable<a class="headerlink" href="#make-the-project-installable" title="Link to this heading"></a></h1>
<p>Making your project installable means that you can build a <em>wheel</em> file and install that
in another environment, just like you installed Flask in your projects environment.
This makes deploying your project the same as installing any other library, so youre
using all the standard Python tools to manage everything.</p>
<p>Installing also comes with other benefits that might not be obvious from
the tutorial or as a new Python user, including:</p>
<ul class="simple">
<li><p>Currently, Python and Flask understand how to use the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code>
package only because youre running from your projects directory.
Installing means you can import it no matter where you run from.</p></li>
<li><p>You can manage your projects dependencies just like other packages
do, so <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">yourproject.whl</span></code> installs them.</p></li>
<li><p>Test tools can isolate your test environment from your development
environment.</p></li>
</ul>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This is being introduced late in the tutorial, but in your future
projects you should always start with this.</p>
</div>
<section id="describe-the-project">
<h2>Describe the Project<a class="headerlink" href="#describe-the-project" title="Link to this heading"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file describes your project and how to build it.</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[project]</span>
<span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&quot;flaskr&quot;</span>
<span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&quot;1.0.0&quot;</span>
<span class="n">description</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&quot;The basic blog app built in the Flask tutorial.&quot;</span>
<span class="n">dependencies</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">&quot;flask&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="k">[build-system]</span>
<span class="n">requires</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;flit_core&lt;4&quot;</span><span class="p">]</span>
<span class="n">build-backend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&quot;flit_core.buildapi&quot;</span>
</pre></div>
</div>
</div>
<p>See the official <a class="reference external" href="https://packaging.python.org/tutorials/packaging-projects/">Packaging tutorial</a> for more
explanation of the files and options used.</p>
</section>
<section id="install-the-project">
<h2>Install the Project<a class="headerlink" href="#install-the-project" title="Link to this heading"></a></h2>
<p>Use <code class="docutils literal notranslate"><span class="pre">pip</span></code> to install your project in the virtual environment.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip install -e .
</pre></div>
</div>
<p>This tells pip to find <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> in the current directory and install the
project in <em>editable</em> or <em>development</em> mode. Editable mode means that as you make
changes to your local code, youll only need to re-install if you change the metadata
about the project, such as its dependencies.</p>
<p>You can observe that the project is now installed with <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">list</span></code>.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip list
Package Version Location
-------------- --------- ----------------------------------
click 6.7
Flask 1.0
flaskr 1.0.0 /home/user/Projects/flask-tutorial
itsdangerous 0.24
Jinja2 2.10
MarkupSafe 1.0
pip 9.0.3
Werkzeug 0.14.1
</pre></div>
</div>
<p>Nothing changes from how youve been running your project so far.
<code class="docutils literal notranslate"><span class="pre">--app</span></code> is still set to <code class="docutils literal notranslate"><span class="pre">flaskr</span></code> and <code class="docutils literal notranslate"><span class="pre">flask</span> <span class="pre">run</span></code> still runs
the application, but you can call it from anywhere, not just the
<code class="docutils literal notranslate"><span class="pre">flask-tutorial</span></code> directory.</p>
<p>Continue to <a class="reference internal" href="tests.html"><span class="doc">Test Coverage</span></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="#">Make the Project Installable</a><ul>
<li><a class="reference internal" href="#describe-the-project">Describe the Project</a></li>
<li><a class="reference internal" href="#install-the-project">Install the Project</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="blog.html" title="previous chapter">Blog Blueprint</a>
<li>Next: <a href="tests.html" title="next chapter">Test Coverage</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,193 @@
<!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>Project Layout &#8212; 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="Application Setup" href="factory.html" />
<link rel="prev" title="Tutorial" href="index.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="factory.html" title="Application Setup"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="index.html" title="Tutorial"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Project Layout</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="project-layout">
<h1>Project Layout<a class="headerlink" href="#project-layout" title="Link to this heading"></a></h1>
<p>Create a project directory and enter it:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ mkdir flask-tutorial
$ cd flask-tutorial
</pre></div>
</div>
<p>Then follow the <a class="reference internal" href="../installation.html"><span class="doc">installation instructions</span></a> to set
up a Python virtual environment and install Flask for your project.</p>
<p>The tutorial will assume youre working from the <code class="docutils literal notranslate"><span class="pre">flask-tutorial</span></code>
directory from now on. The file names at the top of each code block are
relative to this directory.</p>
<hr class="docutils" />
<p>A Flask application can be as simple as a single file.</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">hello.py</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">flask</span><span class="w"> </span><span class="kn">import</span> <span class="n">Flask</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">hello</span><span class="p">():</span>
<span class="k">return</span> <span class="s1">&#39;Hello, World!&#39;</span>
</pre></div>
</div>
</div>
<p>However, as a project gets bigger, it becomes overwhelming to keep all
the code in one file. Python projects use <em>packages</em> to organize code
into multiple modules that can be imported where needed, and the
tutorial will do this as well.</p>
<p>The project directory will contain:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">flaskr/</span></code>, a Python package containing your application code and
files.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">tests/</span></code>, a directory containing test modules.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.venv/</span></code>, a Python virtual environment where Flask and other
dependencies are installed.</p></li>
<li><p>Installation files telling Python how to install your project.</p></li>
<li><p>Version control config, such as <a class="reference external" href="https://git-scm.com/">git</a>. You should make a habit of
using some type of version control for all your projects, no matter
the size.</p></li>
<li><p>Any other project files you might add in the future.</p></li>
</ul>
<p>By the end, your project layout will look like this:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>/home/user/Projects/flask-tutorial
├── flaskr/
│ ├── __init__.py
│ ├── db.py
│ ├── schema.sql
│ ├── auth.py
│ ├── blog.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── auth/
│ │ │ ├── login.html
│ │ │ └── register.html
│ │ └── blog/
│ │ ├── create.html
│ │ ├── index.html
│ │ └── update.html
│ └── static/
│ └── style.css
├── tests/
│ ├── conftest.py
│ ├── data.sql
│ ├── test_factory.py
│ ├── test_db.py
│ ├── test_auth.py
│ └── test_blog.py
├── .venv/
├── pyproject.toml
└── MANIFEST.in
</pre></div>
</div>
<p>If youre using version control, the following files that are generated
while running your project should be ignored. There may be other files
based on the editor you use. In general, ignore files that you didnt
write. For example, with git:</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">.gitignore</span></code></span><a class="headerlink" href="#id2" title="Link to this code"></a></div>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>.venv/
*.pyc
__pycache__/
instance/
.pytest_cache/
.coverage
htmlcov/
dist/
build/
*.egg-info/
</pre></div>
</div>
</div>
<p>Continue to <a class="reference internal" href="factory.html"><span class="doc">Application Setup</span></a>.</p>
</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>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="index.html" title="previous chapter">Tutorial</a>
<li>Next: <a href="factory.html" title="next chapter">Application Setup</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,124 @@
<!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>Keep Developing! &#8212; 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="Templates" href="../templating.html" />
<link rel="prev" title="Deploy to Production" href="deploy.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="../templating.html" title="Templates"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="deploy.html" title="Deploy to Production"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Keep Developing!</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="keep-developing">
<h1>Keep Developing!<a class="headerlink" href="#keep-developing" title="Link to this heading"></a></h1>
<p>Youve learned about quite a few Flask and Python concepts throughout
the tutorial. Go back and review the tutorial and compare your code with
the steps you took to get there. Compare your project to the
<a class="reference external" href="https://github.com/pallets/flask/tree/main/examples/tutorial">example project</a>, which might look a bit
different due to the step-by-step nature of the tutorial.</p>
<p>Theres a lot more to Flask than what youve seen so far. Even so,
youre now equipped to start developing your own web applications. Check
out the <a class="reference internal" href="../quickstart.html"><span class="doc">Quickstart</span></a> for an overview of what Flask can do, then
dive into the docs to keep learning. Flask uses <a class="reference external" href="https://palletsprojects.com/p/jinja/">Jinja</a>, <a class="reference external" href="https://palletsprojects.com/p/click/">Click</a>,
<a class="reference external" href="https://palletsprojects.com/p/werkzeug/">Werkzeug</a>, and <a class="reference external" href="https://palletsprojects.com/p/itsdangerous/">ItsDangerous</a> behind the scenes, and they all have
their own documentation too. Youll also be interested in
<a class="reference internal" href="../extensions.html"><span class="doc">Extensions</span></a> which make tasks like working with the database or
validating form data easier and more powerful.</p>
<p>If you want to keep developing your Flaskr project, here are some ideas
for what to try next:</p>
<ul class="simple">
<li><p>A detail view to show a single post. Click a posts title to go to
its page.</p></li>
<li><p>Like / unlike a post.</p></li>
<li><p>Comments.</p></li>
<li><p>Tags. Clicking a tag shows all the posts with that tag.</p></li>
<li><p>A search box that filters the index page by name.</p></li>
<li><p>Paged display. Only show 5 posts per page.</p></li>
<li><p>Upload an image to go along with a post.</p></li>
<li><p>Format posts using Markdown.</p></li>
<li><p>An RSS feed of new posts.</p></li>
</ul>
<p>Have fun and make awesome applications!</p>
</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>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="deploy.html" title="previous chapter">Deploy to Production</a>
<li>Next: <a href="../templating.html" title="next chapter">Templates</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,152 @@
<!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>Static Files &#8212; 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="Blog Blueprint" href="blog.html" />
<link rel="prev" title="Templates" href="templates.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="blog.html" title="Blog Blueprint"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="templates.html" title="Templates"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Static Files</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="static-files">
<h1>Static Files<a class="headerlink" href="#static-files" title="Link to this heading"></a></h1>
<p>The authentication views and templates work, but they look very plain
right now. Some <a class="reference external" href="https://developer.mozilla.org/docs/Web/CSS">CSS</a> can be added to add style to the HTML layout you
constructed. The style wont change, so its a <em>static</em> file rather than
a template.</p>
<p>Flask automatically adds a <code class="docutils literal notranslate"><span class="pre">static</span></code> view that takes a path relative
to the <code class="docutils literal notranslate"><span class="pre">flaskr/static</span></code> directory and serves it. The <code class="docutils literal notranslate"><span class="pre">base.html</span></code>
template already has a link to the <code class="docutils literal notranslate"><span class="pre">style.css</span></code> file:</p>
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;static&#39;</span><span class="o">,</span> <span class="nv">filename</span><span class="o">=</span><span class="s1">&#39;style.css&#39;</span><span class="o">)</span> <span class="cp">}}</span>
</pre></div>
</div>
<p>Besides CSS, other types of static files might be files with JavaScript
functions, or a logo image. They are all placed under the
<code class="docutils literal notranslate"><span class="pre">flaskr/static</span></code> directory and referenced with
<code class="docutils literal notranslate"><span class="pre">url_for('static',</span> <span class="pre">filename='...')</span></code>.</p>
<p>This tutorial isnt focused on how to write CSS, so you can just copy
the following into the <code class="docutils literal notranslate"><span class="pre">flaskr/static/style.css</span></code> file:</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/static/style.css</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-css notranslate"><div class="highlight"><pre><span></span><span class="nt">html</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">font-family</span><span class="p">:</span><span class="w"> </span><span class="kc">sans-serif</span><span class="p">;</span><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="mh">#eee</span><span class="p">;</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">rem</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">body</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">max-width</span><span class="p">:</span><span class="w"> </span><span class="mi">960</span><span class="kt">px</span><span class="p">;</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="kc">white</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">h1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">font-family</span><span class="p">:</span><span class="w"> </span><span class="kc">serif</span><span class="p">;</span><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="mh">#377ba8</span><span class="p">;</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">rem</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">a</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="mh">#377ba8</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">hr</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">border</span><span class="p">:</span><span class="w"> </span><span class="kc">none</span><span class="p">;</span><span class="w"> </span><span class="k">border-top</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="kc">lightgray</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">nav</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="kc">lightgray</span><span class="p">;</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">nav</span><span class="w"> </span><span class="nt">h1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">flex</span><span class="p">:</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">nav</span><span class="w"> </span><span class="nt">h1</span><span class="w"> </span><span class="nt">a</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">text-decoration</span><span class="p">:</span><span class="w"> </span><span class="kc">none</span><span class="p">;</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mf">0.25</span><span class="kt">rem</span><span class="w"> </span><span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">nav</span><span class="w"> </span><span class="nt">ul</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span><span class="w"> </span><span class="k">list-style</span><span class="p">:</span><span class="w"> </span><span class="kc">none</span><span class="p">;</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">nav</span><span class="w"> </span><span class="nt">ul</span><span class="w"> </span><span class="nt">li</span><span class="w"> </span><span class="nt">a</span><span class="o">,</span><span class="w"> </span><span class="nt">nav</span><span class="w"> </span><span class="nt">ul</span><span class="w"> </span><span class="nt">li</span><span class="w"> </span><span class="nt">span</span><span class="o">,</span><span class="w"> </span><span class="nt">header</span><span class="w"> </span><span class="p">.</span><span class="nc">action</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">block</span><span class="p">;</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">1</span><span class="kt">rem</span><span class="w"> </span><span class="mi">1</span><span class="kt">rem</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">header</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">border-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="kc">lightgray</span><span class="p">;</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">flex-end</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">header</span><span class="w"> </span><span class="nt">h1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">flex</span><span class="p">:</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">rem</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mf">0.25</span><span class="kt">rem</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">flash</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">em</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="mh">#cae6f6</span><span class="p">;</span><span class="w"> </span><span class="k">border</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="mh">#377ba8</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">post</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">header</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">flex-end</span><span class="p">;</span><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mf">0.85</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">post</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">header</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">div</span><span class="p">:</span><span class="nd">first-of-type</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">flex</span><span class="p">:</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">post</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">header</span><span class="w"> </span><span class="nt">h1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">post</span><span class="w"> </span><span class="p">.</span><span class="nc">about</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="kc">slategray</span><span class="p">;</span><span class="w"> </span><span class="k">font-style</span><span class="p">:</span><span class="w"> </span><span class="kc">italic</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">post</span><span class="w"> </span><span class="p">.</span><span class="nc">body</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre-line</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="p">:</span><span class="nd">last-child</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="nt">form</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">em</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span><span class="w"> </span><span class="k">flex-direction</span><span class="p">:</span><span class="w"> </span><span class="kc">column</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="nt">label</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="kc">bold</span><span class="p">;</span><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mf">0.5</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="nt">input</span><span class="o">,</span><span class="w"> </span><span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="nt">textarea</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="p">.</span><span class="nc">content</span><span class="w"> </span><span class="nt">textarea</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">min-height</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="k">resize</span><span class="p">:</span><span class="w"> </span><span class="kc">vertical</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">input</span><span class="p">.</span><span class="nc">danger</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="mh">#cc2f2e</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="nt">input</span><span class="o">[</span><span class="nt">type</span><span class="o">=</span><span class="nt">submit</span><span class="o">]</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">align-self</span><span class="p">:</span><span class="w"> </span><span class="kc">start</span><span class="p">;</span><span class="w"> </span><span class="k">min-width</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
</pre></div>
</div>
</div>
<p>You can find a less compact version of <code class="docutils literal notranslate"><span class="pre">style.css</span></code> in the
<a class="reference external" href="https://github.com/pallets/flask/tree/main/examples/tutorial/flaskr/static/style.css">example code</a>.</p>
<p>Go to <a class="reference external" href="http://127.0.0.1:5000/auth/login">http://127.0.0.1:5000/auth/login</a> and the page should look like the
screenshot below.</p>
<img alt="screenshot of login page" class="screenshot align-center" src="../_images/flaskr_login.png" />
<p>You can read more about CSS from <a class="reference external" href="https://developer.mozilla.org/docs/Web/CSS">Mozillas documentation</a>. If
you change a static file, refresh the browser page. If the change
doesnt show up, try clearing your browsers cache.</p>
<p>Continue to <a class="reference internal" href="blog.html"><span class="doc">Blog Blueprint</span></a>.</p>
</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>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="templates.html" title="previous chapter">Templates</a>
<li>Next: <a href="blog.html" title="next chapter">Blog Blueprint</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,268 @@
<!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>Templates &#8212; 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="Static Files" href="static.html" />
<link rel="prev" title="Blueprints and Views" href="views.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="static.html" title="Static Files"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="views.html" title="Blueprints and Views"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Templates</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="templates">
<h1>Templates<a class="headerlink" href="#templates" title="Link to this heading"></a></h1>
<p>Youve written the authentication views for your application, but if
youre running the server and try to go to any of the URLs, youll see a
<code class="docutils literal notranslate"><span class="pre">TemplateNotFound</span></code> error. Thats because the views are calling
<a class="reference internal" href="../api.html#flask.render_template" title="flask.render_template"><code class="xref py py-func docutils literal notranslate"><span class="pre">render_template()</span></code></a>, but you havent written the templates yet.
The template files will be stored in the <code class="docutils literal notranslate"><span class="pre">templates</span></code> directory inside
the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code> package.</p>
<p>Templates are files that contain static data as well as placeholders
for dynamic data. A template is rendered with specific data to produce a
final document. Flask uses the <a class="reference external" href="https://jinja.palletsprojects.com/templates/">Jinja</a> template library to render
templates.</p>
<p>In your application, you will use templates to render <a class="reference external" href="https://developer.mozilla.org/docs/Web/HTML">HTML</a> which
will display in the users browser. In Flask, Jinja is configured to
<em>autoescape</em> any data that is rendered in HTML templates. This means
that its safe to render user input; any characters theyve entered that
could mess with the HTML, such as <code class="docutils literal notranslate"><span class="pre">&lt;</span></code> and <code class="docutils literal notranslate"><span class="pre">&gt;</span></code> will be <em>escaped</em> with
<em>safe</em> values that look the same in the browser but dont cause unwanted
effects.</p>
<p>Jinja looks and behaves mostly like Python. Special delimiters are used
to distinguish Jinja syntax from the static data in the template.
Anything between <code class="docutils literal notranslate"><span class="pre">{{</span></code> and <code class="docutils literal notranslate"><span class="pre">}}</span></code> is an expression that will be output
to the final document. <code class="docutils literal notranslate"><span class="pre">{%</span></code> and <code class="docutils literal notranslate"><span class="pre">%}</span></code> denotes a control flow
statement like <code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">for</span></code>. Unlike Python, blocks are denoted
by start and end tags rather than indentation since static text within
a block could change indentation.</p>
<section id="the-base-layout">
<h2>The Base Layout<a class="headerlink" href="#the-base-layout" title="Link to this heading"></a></h2>
<p>Each page in the application will have the same basic layout around a
different body. Instead of writing the entire HTML structure in each
template, each template will <em>extend</em> a base template and override
specific sections.</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/templates/base.html</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="cp">&lt;!doctype html&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span> - Flaskr<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&quot;stylesheet&quot;</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;static&#39;</span><span class="o">,</span> <span class="nv">filename</span><span class="o">=</span><span class="s1">&#39;style.css&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">nav</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Flaskr<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">g.user</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">span</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">g.user</span><span class="o">[</span><span class="s1">&#39;username&#39;</span><span class="o">]</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;auth.logout&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span>Log Out<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">else</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;auth.register&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span>Register<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;auth.login&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span>Log In<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">section</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;content&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">header</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">header</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">message</span> <span class="k">in</span> <span class="nv">get_flashed_messages</span><span class="o">()</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;flash&quot;</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">message</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="p">&lt;/</span><span class="nt">section</span><span class="p">&gt;</span>
</pre></div>
</div>
</div>
<p><a class="reference internal" href="../api.html#flask.g" title="flask.g"><code class="xref py py-data docutils literal notranslate"><span class="pre">g</span></code></a> is automatically available in templates. Based on if
<code class="docutils literal notranslate"><span class="pre">g.user</span></code> is set (from <code class="docutils literal notranslate"><span class="pre">load_logged_in_user</span></code>), either the username
and a log out link are displayed, or links to register and log in
are displayed. <a class="reference internal" href="../api.html#flask.url_for" title="flask.url_for"><code class="xref py py-func docutils literal notranslate"><span class="pre">url_for()</span></code></a> is also automatically available, and is
used to generate URLs to views instead of writing them out manually.</p>
<p>After the page title, and before the content, the template loops over
each message returned by <a class="reference internal" href="../api.html#flask.get_flashed_messages" title="flask.get_flashed_messages"><code class="xref py py-func docutils literal notranslate"><span class="pre">get_flashed_messages()</span></code></a>. You used
<a class="reference internal" href="../api.html#flask.flash" title="flask.flash"><code class="xref py py-func docutils literal notranslate"><span class="pre">flash()</span></code></a> in the views to show error messages, and this is the code
that will display them.</p>
<p>There are three blocks defined here that will be overridden in the other
templates:</p>
<ol class="arabic simple">
<li><p><code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">title</span> <span class="pre">%}</span></code> will change the title displayed in the
browsers tab and window title.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">header</span> <span class="pre">%}</span></code> is similar to <code class="docutils literal notranslate"><span class="pre">title</span></code> but will change the
title displayed on the page.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">content</span> <span class="pre">%}</span></code> is where the content of each page goes, such
as the login form or a blog post.</p></li>
</ol>
<p>The base template is directly in the <code class="docutils literal notranslate"><span class="pre">templates</span></code> directory. To keep
the others organized, the templates for a blueprint will be placed in a
directory with the same name as the blueprint.</p>
</section>
<section id="register">
<h2>Register<a class="headerlink" href="#register" title="Link to this heading"></a></h2>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/templates/auth/register.html</span></code></span><a class="headerlink" href="#id2" title="Link to this code"></a></div>
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s1">&#39;base.html&#39;</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">header</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Register<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">form</span> <span class="na">method</span><span class="o">=</span><span class="s">&quot;post&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&quot;username&quot;</span><span class="p">&gt;</span>Username<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;username&quot;</span> <span class="na">id</span><span class="o">=</span><span class="s">&quot;username&quot;</span> <span class="na">required</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&quot;password&quot;</span><span class="p">&gt;</span>Password<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&quot;password&quot;</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;password&quot;</span> <span class="na">id</span><span class="o">=</span><span class="s">&quot;password&quot;</span> <span class="na">required</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&quot;submit&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;Register&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
</div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">extends</span> <span class="pre">'base.html'</span> <span class="pre">%}</span></code> tells Jinja that this template should
replace the blocks from the base template. All the rendered content must
appear inside <code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">%}</span></code> tags that override blocks from the base
template.</p>
<p>A useful pattern used here is to place <code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">title</span> <span class="pre">%}</span></code> inside
<code class="docutils literal notranslate"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">header</span> <span class="pre">%}</span></code>. This will set the title block and then output
the value of it into the header block, so that both the window and page
share the same title without writing it twice.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">input</span></code> tags are using the <code class="docutils literal notranslate"><span class="pre">required</span></code> attribute here. This tells
the browser not to submit the form until those fields are filled in. If
the user is using an older browser that doesnt support that attribute,
or if they are using something besides a browser to make requests, you
still want to validate the data in the Flask view. Its important to
always fully validate the data on the server, even if the client does
some validation as well.</p>
</section>
<section id="log-in">
<h2>Log In<a class="headerlink" href="#log-in" title="Link to this heading"></a></h2>
<p>This is identical to the register template except for the title and
submit button.</p>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/templates/auth/login.html</span></code></span><a class="headerlink" href="#id3" title="Link to this code"></a></div>
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s1">&#39;base.html&#39;</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">header</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Log In<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">form</span> <span class="na">method</span><span class="o">=</span><span class="s">&quot;post&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&quot;username&quot;</span><span class="p">&gt;</span>Username<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;username&quot;</span> <span class="na">id</span><span class="o">=</span><span class="s">&quot;username&quot;</span> <span class="na">required</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&quot;password&quot;</span><span class="p">&gt;</span>Password<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&quot;password&quot;</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;password&quot;</span> <span class="na">id</span><span class="o">=</span><span class="s">&quot;password&quot;</span> <span class="na">required</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&quot;submit&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;Log In&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
</div>
</div>
</section>
<section id="register-a-user">
<h2>Register A User<a class="headerlink" href="#register-a-user" title="Link to this heading"></a></h2>
<p>Now that the authentication templates are written, you can register a
user. Make sure the server is still running (<code class="docutils literal notranslate"><span class="pre">flask</span> <span class="pre">run</span></code> if its not),
then go to <a class="reference external" href="http://127.0.0.1:5000/auth/register">http://127.0.0.1:5000/auth/register</a>.</p>
<p>Try clicking the “Register” button without filling out the form and see
that the browser shows an error message. Try removing the <code class="docutils literal notranslate"><span class="pre">required</span></code>
attributes from the <code class="docutils literal notranslate"><span class="pre">register.html</span></code> template and click “Register”
again. Instead of the browser showing an error, the page will reload and
the error from <a class="reference internal" href="../api.html#flask.flash" title="flask.flash"><code class="xref py py-func docutils literal notranslate"><span class="pre">flash()</span></code></a> in the view will be shown.</p>
<p>Fill out a username and password and youll be redirected to the login
page. Try entering an incorrect username, or the correct username and
incorrect password. If you log in youll get an error because theres
no <code class="docutils literal notranslate"><span class="pre">index</span></code> view to redirect to yet.</p>
<p>Continue to <a class="reference internal" href="static.html"><span class="doc">Static Files</span></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="#">Templates</a><ul>
<li><a class="reference internal" href="#the-base-layout">The Base Layout</a></li>
<li><a class="reference internal" href="#register">Register</a></li>
<li><a class="reference internal" href="#log-in">Log In</a></li>
<li><a class="reference internal" href="#register-a-user">Register A User</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="views.html" title="previous chapter">Blueprints and Views</a>
<li>Next: <a href="static.html" title="next chapter">Static Files</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,622 @@
<!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>Test Coverage &#8212; 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="Deploy to Production" href="deploy.html" />
<link rel="prev" title="Make the Project Installable" href="install.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="deploy.html" title="Deploy to Production"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="install.html" title="Make the Project Installable"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Test Coverage</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="test-coverage">
<h1>Test Coverage<a class="headerlink" href="#test-coverage" title="Link to this heading"></a></h1>
<p>Writing unit tests for your application lets you check that the code
you wrote works the way you expect. Flask provides a test client that
simulates requests to the application and returns the response data.</p>
<p>You should test as much of your code as possible. Code in functions only
runs when the function is called, and code in branches, such as <code class="docutils literal notranslate"><span class="pre">if</span></code>
blocks, only runs when the condition is met. You want to make sure that
each function is tested with data that covers each branch.</p>
<p>The closer you get to 100% coverage, the more comfortable you can be
that making a change wont unexpectedly change other behavior. However,
100% coverage doesnt guarantee that your application doesnt have bugs.
In particular, it doesnt test how the user interacts with the
application in the browser. Despite this, test coverage is an important
tool to use during development.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This is being introduced late in the tutorial, but in your future
projects you should test as you develop.</p>
</div>
<p>Youll use <a class="reference external" href="https://pytest.readthedocs.io/">pytest</a> and <a class="reference external" href="https://coverage.readthedocs.io/">coverage</a> to test and measure your code.
Install them both:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip install pytest coverage
</pre></div>
</div>
<section id="setup-and-fixtures">
<h2>Setup and Fixtures<a class="headerlink" href="#setup-and-fixtures" title="Link to this heading"></a></h2>
<p>The test code is located in the <code class="docutils literal notranslate"><span class="pre">tests</span></code> directory. This directory is
<em>next to</em> the <code class="docutils literal notranslate"><span class="pre">flaskr</span></code> package, not inside it. The
<code class="docutils literal notranslate"><span class="pre">tests/conftest.py</span></code> file contains setup functions called <em>fixtures</em>
that each test will use. Tests are in Python modules that start with
<code class="docutils literal notranslate"><span class="pre">test_</span></code>, and each test function in those modules also starts with
<code class="docutils literal notranslate"><span class="pre">test_</span></code>.</p>
<p>Each test will create a new temporary database file and populate some
data that will be used in the tests. Write a SQL file to insert that
data.</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/data.sql</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="p">(</span><span class="n">username</span><span class="p">,</span><span class="w"> </span><span class="n">password</span><span class="p">)</span>
<span class="k">VALUES</span>
<span class="w"> </span><span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f&#39;</span><span class="p">),</span>
<span class="w"> </span><span class="p">(</span><span class="s1">&#39;other&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79&#39;</span><span class="p">);</span>
<span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="p">(</span><span class="n">title</span><span class="p">,</span><span class="w"> </span><span class="n">body</span><span class="p">,</span><span class="w"> </span><span class="n">author_id</span><span class="p">,</span><span class="w"> </span><span class="n">created</span><span class="p">)</span>
<span class="k">VALUES</span>
<span class="w"> </span><span class="p">(</span><span class="s1">&#39;test title&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;test&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">x</span><span class="s1">&#39;0a&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;body&#39;</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;2018-01-01 00:00:00&#39;</span><span class="p">);</span>
</pre></div>
</div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">app</span></code> fixture will call the factory and pass <code class="docutils literal notranslate"><span class="pre">test_config</span></code> to
configure the application and database for testing instead of using your
local development configuration.</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/conftest.py</span></code></span><a class="headerlink" href="#id2" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">tempfile</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flaskr</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_app</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flaskr.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_db</span><span class="p">,</span> <span class="n">init_db</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> <span class="s1">&#39;data.sql&#39;</span><span class="p">),</span> <span class="s1">&#39;rb&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">_data_sql</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf8&#39;</span><span class="p">)</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
<span class="k">def</span><span class="w"> </span><span class="nf">app</span><span class="p">():</span>
<span class="n">db_fd</span><span class="p">,</span> <span class="n">db_path</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">mkstemp</span><span class="p">()</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">create_app</span><span class="p">({</span>
<span class="s1">&#39;TESTING&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s1">&#39;DATABASE&#39;</span><span class="p">:</span> <span class="n">db_path</span><span class="p">,</span>
<span class="p">})</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="n">init_db</span><span class="p">()</span>
<span class="n">get_db</span><span class="p">()</span><span class="o">.</span><span class="n">executescript</span><span class="p">(</span><span class="n">_data_sql</span><span class="p">)</span>
<span class="k">yield</span> <span class="n">app</span>
<span class="n">os</span><span class="o">.</span><span class="n">close</span><span class="p">(</span><span class="n">db_fd</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">db_path</span><span class="p">)</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
<span class="k">def</span><span class="w"> </span><span class="nf">client</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
<span class="k">return</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
<span class="k">def</span><span class="w"> </span><span class="nf">runner</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
<span class="k">return</span> <span class="n">app</span><span class="o">.</span><span class="n">test_cli_runner</span><span class="p">()</span>
</pre></div>
</div>
</div>
<p><a class="reference external" href="https://docs.python.org/3/library/tempfile.html#tempfile.mkstemp" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">tempfile.mkstemp()</span></code></a> creates and opens a temporary file, returning
the file descriptor and the path to it. The <code class="docutils literal notranslate"><span class="pre">DATABASE</span></code> path is
overridden so it points to this temporary path instead of the instance
folder. After setting the path, the database tables are created and the
test data is inserted. After the test is over, the temporary file is
closed and removed.</p>
<p><a class="reference internal" href="../config.html#TESTING" title="TESTING"><code class="xref py py-data docutils literal notranslate"><span class="pre">TESTING</span></code></a> tells Flask that the app is in test mode. Flask changes
some internal behavior so its easier to test, and other extensions can
also use the flag to make testing them easier.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">client</span></code> fixture calls
<a class="reference internal" href="../api.html#flask.Flask.test_client" title="flask.Flask.test_client"><code class="xref py py-meth docutils literal notranslate"><span class="pre">app.test_client()</span></code></a> with the application
object created by the <code class="docutils literal notranslate"><span class="pre">app</span></code> fixture. Tests will use the client to make
requests to the application without running the server.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">runner</span></code> fixture is similar to <code class="docutils literal notranslate"><span class="pre">client</span></code>.
<a class="reference internal" href="../api.html#flask.Flask.test_cli_runner" title="flask.Flask.test_cli_runner"><code class="xref py py-meth docutils literal notranslate"><span class="pre">app.test_cli_runner()</span></code></a> creates a runner
that can call the Click commands registered with the application.</p>
<p>Pytest uses fixtures by matching their function names with the names
of arguments in the test functions. For example, the <code class="docutils literal notranslate"><span class="pre">test_hello</span></code>
function youll write next takes a <code class="docutils literal notranslate"><span class="pre">client</span></code> argument. Pytest matches
that with the <code class="docutils literal notranslate"><span class="pre">client</span></code> fixture function, calls it, and passes the
returned value to the test function.</p>
</section>
<section id="factory">
<h2>Factory<a class="headerlink" href="#factory" title="Link to this heading"></a></h2>
<p>Theres not much to test about the factory itself. Most of the code will
be executed for each test already, so if something fails the other tests
will notice.</p>
<p>The only behavior that can change is passing test config. If config is
not passed, there should be some default configuration, otherwise the
configuration should be overridden.</p>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_factory.py</span></code></span><a class="headerlink" href="#id3" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">flaskr</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_app</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_config</span><span class="p">():</span>
<span class="k">assert</span> <span class="ow">not</span> <span class="n">create_app</span><span class="p">()</span><span class="o">.</span><span class="n">testing</span>
<span class="k">assert</span> <span class="n">create_app</span><span class="p">({</span><span class="s1">&#39;TESTING&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">})</span><span class="o">.</span><span class="n">testing</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_hello</span><span class="p">(</span><span class="n">client</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/hello&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span> <span class="o">==</span> <span class="sa">b</span><span class="s1">&#39;Hello, World!&#39;</span>
</pre></div>
</div>
</div>
<p>You added the <code class="docutils literal notranslate"><span class="pre">hello</span></code> route as an example when writing the factory at
the beginning of the tutorial. It returns “Hello, World!”, so the test
checks that the response data matches.</p>
</section>
<section id="database">
<h2>Database<a class="headerlink" href="#database" title="Link to this heading"></a></h2>
<p>Within an application context, <code class="docutils literal notranslate"><span class="pre">get_db</span></code> should return the same
connection each time its called. After the context, the connection
should be closed.</p>
<div class="literal-block-wrapper docutils container" id="id4">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_db.py</span></code></span><a class="headerlink" href="#id4" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">sqlite3</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flaskr.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_db</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_get_close_db</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">db</span> <span class="ow">is</span> <span class="n">get_db</span><span class="p">()</span>
<span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="n">sqlite3</span><span class="o">.</span><span class="n">ProgrammingError</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">&#39;SELECT 1&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="s1">&#39;closed&#39;</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">init-db</span></code> command should call the <code class="docutils literal notranslate"><span class="pre">init_db</span></code> function and output
a message.</p>
<div class="literal-block-wrapper docutils container" id="id5">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_db.py</span></code></span><a class="headerlink" href="#id5" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">test_init_db_command</span><span class="p">(</span><span class="n">runner</span><span class="p">,</span> <span class="n">monkeypatch</span><span class="p">):</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Recorder</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="n">called</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">def</span><span class="w"> </span><span class="nf">fake_init_db</span><span class="p">():</span>
<span class="n">Recorder</span><span class="o">.</span><span class="n">called</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">monkeypatch</span><span class="o">.</span><span class="n">setattr</span><span class="p">(</span><span class="s1">&#39;flaskr.db.init_db&#39;</span><span class="p">,</span> <span class="n">fake_init_db</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">runner</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;init-db&#39;</span><span class="p">])</span>
<span class="k">assert</span> <span class="s1">&#39;Initialized&#39;</span> <span class="ow">in</span> <span class="n">result</span><span class="o">.</span><span class="n">output</span>
<span class="k">assert</span> <span class="n">Recorder</span><span class="o">.</span><span class="n">called</span>
</pre></div>
</div>
</div>
<p>This test uses Pytests <code class="docutils literal notranslate"><span class="pre">monkeypatch</span></code> fixture to replace the
<code class="docutils literal notranslate"><span class="pre">init_db</span></code> function with one that records that its been called. The
<code class="docutils literal notranslate"><span class="pre">runner</span></code> fixture you wrote above is used to call the <code class="docutils literal notranslate"><span class="pre">init-db</span></code>
command by name.</p>
</section>
<section id="authentication">
<h2>Authentication<a class="headerlink" href="#authentication" title="Link to this heading"></a></h2>
<p>For most of the views, a user needs to be logged in. The easiest way to
do this in tests is to make a <code class="docutils literal notranslate"><span class="pre">POST</span></code> request to the <code class="docutils literal notranslate"><span class="pre">login</span></code> view
with the client. Rather than writing that out every time, you can write
a class with methods to do that, and use a fixture to pass it the client
for each test.</p>
<div class="literal-block-wrapper docutils container" id="id6">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/conftest.py</span></code></span><a class="headerlink" href="#id6" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">AuthActions</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">client</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_client</span> <span class="o">=</span> <span class="n">client</span>
<span class="k">def</span><span class="w"> </span><span class="nf">login</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s1">&#39;test&#39;</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span>
<span class="s1">&#39;/auth/login&#39;</span><span class="p">,</span>
<span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;username&#39;</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">:</span> <span class="n">password</span><span class="p">}</span>
<span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">logout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/auth/logout&#39;</span><span class="p">)</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
<span class="k">def</span><span class="w"> </span><span class="nf">auth</span><span class="p">(</span><span class="n">client</span><span class="p">):</span>
<span class="k">return</span> <span class="n">AuthActions</span><span class="p">(</span><span class="n">client</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>With the <code class="docutils literal notranslate"><span class="pre">auth</span></code> fixture, you can call <code class="docutils literal notranslate"><span class="pre">auth.login()</span></code> in a test to
log in as the <code class="docutils literal notranslate"><span class="pre">test</span></code> user, which was inserted as part of the test
data in the <code class="docutils literal notranslate"><span class="pre">app</span></code> fixture.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">register</span></code> view should render successfully on <code class="docutils literal notranslate"><span class="pre">GET</span></code>. On <code class="docutils literal notranslate"><span class="pre">POST</span></code>
with valid form data, it should redirect to the login URL and the users
data should be in the database. Invalid data should display error
messages.</p>
<div class="literal-block-wrapper docutils container" id="id7">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_auth.py</span></code></span><a class="headerlink" href="#id7" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flask</span><span class="w"> </span><span class="kn">import</span> <span class="n">g</span><span class="p">,</span> <span class="n">session</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flaskr.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_db</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_register</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/auth/register&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span>
<span class="s1">&#39;/auth/register&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;username&#39;</span><span class="p">:</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">:</span> <span class="s1">&#39;a&#39;</span><span class="p">}</span>
<span class="p">)</span>
<span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Location&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;/auth/login&quot;</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="k">assert</span> <span class="n">get_db</span><span class="p">()</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="s2">&quot;SELECT * FROM user WHERE username = &#39;a&#39;&quot;</span><span class="p">,</span>
<span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">parametrize</span><span class="p">((</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="s1">&#39;message&#39;</span><span class="p">),</span> <span class="p">(</span>
<span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;Username is required.&#39;</span><span class="p">),</span>
<span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;Password is required.&#39;</span><span class="p">),</span>
<span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;already registered&#39;</span><span class="p">),</span>
<span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_register_validate_input</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span>
<span class="s1">&#39;/auth/register&#39;</span><span class="p">,</span>
<span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;username&#39;</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">:</span> <span class="n">password</span><span class="p">}</span>
<span class="p">)</span>
<span class="k">assert</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
</pre></div>
</div>
</div>
<p><a class="reference external" href="https://werkzeug.palletsprojects.com/en/stable/test/#werkzeug.test.Client.get" title="(in Werkzeug v3.1.x)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">client.get()</span></code></a> makes a <code class="docutils literal notranslate"><span class="pre">GET</span></code> request
and returns the <a class="reference internal" href="../api.html#flask.Response" title="flask.Response"><code class="xref py py-class docutils literal notranslate"><span class="pre">Response</span></code></a> object returned by Flask. Similarly,
<a class="reference external" href="https://werkzeug.palletsprojects.com/en/stable/test/#werkzeug.test.Client.post" title="(in Werkzeug v3.1.x)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">client.post()</span></code></a> makes a <code class="docutils literal notranslate"><span class="pre">POST</span></code>
request, converting the <code class="docutils literal notranslate"><span class="pre">data</span></code> dict into form data.</p>
<p>To test that the page renders successfully, a simple request is made and
checked for a <code class="docutils literal notranslate"><span class="pre">200</span> <span class="pre">OK</span></code> <a class="reference internal" href="../api.html#flask.Response.status_code" title="flask.Response.status_code"><code class="xref py py-attr docutils literal notranslate"><span class="pre">status_code</span></code></a>. If
rendering failed, Flask would return a <code class="docutils literal notranslate"><span class="pre">500</span> <span class="pre">Internal</span> <span class="pre">Server</span> <span class="pre">Error</span></code>
code.</p>
<p><code class="xref py py-attr docutils literal notranslate"><span class="pre">headers</span></code> will have a <code class="docutils literal notranslate"><span class="pre">Location</span></code> header with the login
URL when the register view redirects to the login view.</p>
<p><a class="reference internal" href="../api.html#flask.Response.data" title="flask.Response.data"><code class="xref py py-attr docutils literal notranslate"><span class="pre">data</span></code></a> contains the body of the response as bytes. If
you expect a certain value to render on the page, check that its in
<code class="docutils literal notranslate"><span class="pre">data</span></code>. Bytes must be compared to bytes. If you want to compare text,
use <a class="reference external" href="https://werkzeug.palletsprojects.com/en/stable/wrappers/#werkzeug.wrappers.Response.get_data" title="(in Werkzeug v3.1.x)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_data(as_text=True)</span></code></a>
instead.</p>
<p><code class="docutils literal notranslate"><span class="pre">pytest.mark.parametrize</span></code> tells Pytest to run the same test function
with different arguments. You use it here to test different invalid
input and error messages without writing the same code three times.</p>
<p>The tests for the <code class="docutils literal notranslate"><span class="pre">login</span></code> view are very similar to those for
<code class="docutils literal notranslate"><span class="pre">register</span></code>. Rather than testing the data in the database,
<a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a> should have <code class="docutils literal notranslate"><span class="pre">user_id</span></code> set after logging in.</p>
<div class="literal-block-wrapper docutils container" id="id8">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_auth.py</span></code></span><a class="headerlink" href="#id8" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">test_login</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">):</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/auth/login&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Location&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;/&quot;</span>
<span class="k">with</span> <span class="n">client</span><span class="p">:</span>
<span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">session</span><span class="p">[</span><span class="s1">&#39;user_id&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="k">assert</span> <span class="n">g</span><span class="o">.</span><span class="n">user</span><span class="p">[</span><span class="s1">&#39;username&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;test&#39;</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">parametrize</span><span class="p">((</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="s1">&#39;message&#39;</span><span class="p">),</span> <span class="p">(</span>
<span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;Incorrect username.&#39;</span><span class="p">),</span>
<span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;Incorrect password.&#39;</span><span class="p">),</span>
<span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_login_validate_input</span><span class="p">(</span><span class="n">auth</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
</pre></div>
</div>
</div>
<p>Using <code class="docutils literal notranslate"><span class="pre">client</span></code> in a <code class="docutils literal notranslate"><span class="pre">with</span></code> block allows accessing context variables
such as <a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a> after the response is returned. Normally,
accessing <code class="docutils literal notranslate"><span class="pre">session</span></code> outside of a request would raise an error.</p>
<p>Testing <code class="docutils literal notranslate"><span class="pre">logout</span></code> is the opposite of <code class="docutils literal notranslate"><span class="pre">login</span></code>. <a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a> should
not contain <code class="docutils literal notranslate"><span class="pre">user_id</span></code> after logging out.</p>
<div class="literal-block-wrapper docutils container" id="id9">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_auth.py</span></code></span><a class="headerlink" href="#id9" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">test_logout</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">):</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="k">with</span> <span class="n">client</span><span class="p">:</span>
<span class="n">auth</span><span class="o">.</span><span class="n">logout</span><span class="p">()</span>
<span class="k">assert</span> <span class="s1">&#39;user_id&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">session</span>
</pre></div>
</div>
</div>
</section>
<section id="blog">
<h2>Blog<a class="headerlink" href="#blog" title="Link to this heading"></a></h2>
<p>All the blog views use the <code class="docutils literal notranslate"><span class="pre">auth</span></code> fixture you wrote earlier. Call
<code class="docutils literal notranslate"><span class="pre">auth.login()</span></code> and subsequent requests from the client will be logged
in as the <code class="docutils literal notranslate"><span class="pre">test</span></code> user.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">index</span></code> view should display information about the post that was
added with the test data. When logged in as the author, there should be
a link to edit the post.</p>
<p>You can also test some more authentication behavior while testing the
<code class="docutils literal notranslate"><span class="pre">index</span></code> view. When not logged in, each page shows links to log in or
register. When logged in, theres a link to log out.</p>
<div class="literal-block-wrapper docutils container" id="id10">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_blog.py</span></code></span><a class="headerlink" href="#id10" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flaskr.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_db</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_index</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s2">&quot;Log In&quot;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s2">&quot;Register&quot;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;Log Out&#39;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;test title&#39;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;by test on 2018-01-01&#39;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;test</span><span class="se">\n</span><span class="s1">body&#39;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;href=&quot;/1/update&quot;&#39;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
</pre></div>
</div>
</div>
<p>A user must be logged in to access the <code class="docutils literal notranslate"><span class="pre">create</span></code>, <code class="docutils literal notranslate"><span class="pre">update</span></code>, and
<code class="docutils literal notranslate"><span class="pre">delete</span></code> views. The logged in user must be the author of the post to
access <code class="docutils literal notranslate"><span class="pre">update</span></code> and <code class="docutils literal notranslate"><span class="pre">delete</span></code>, otherwise a <code class="docutils literal notranslate"><span class="pre">403</span> <span class="pre">Forbidden</span></code> status
is returned. If a <code class="docutils literal notranslate"><span class="pre">post</span></code> with the given <code class="docutils literal notranslate"><span class="pre">id</span></code> doesnt exist,
<code class="docutils literal notranslate"><span class="pre">update</span></code> and <code class="docutils literal notranslate"><span class="pre">delete</span></code> should return <code class="docutils literal notranslate"><span class="pre">404</span> <span class="pre">Not</span> <span class="pre">Found</span></code>.</p>
<div class="literal-block-wrapper docutils container" id="id11">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_blog.py</span></code></span><a class="headerlink" href="#id11" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">parametrize</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">,</span> <span class="p">(</span>
<span class="s1">&#39;/create&#39;</span><span class="p">,</span>
<span class="s1">&#39;/1/update&#39;</span><span class="p">,</span>
<span class="s1">&#39;/1/delete&#39;</span><span class="p">,</span>
<span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_login_required</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Location&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;/auth/login&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_author_required</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">):</span>
<span class="c1"># change the post author to another user</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">&#39;UPDATE post SET author_id = 2 WHERE id = 1&#39;</span><span class="p">)</span>
<span class="n">db</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="c1"># current user can&#39;t modify other user&#39;s post</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s1">&#39;/1/update&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">403</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s1">&#39;/1/delete&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">403</span>
<span class="c1"># current user doesn&#39;t see edit link</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;href=&quot;/1/update&quot;&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">data</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">parametrize</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">,</span> <span class="p">(</span>
<span class="s1">&#39;/2/update&#39;</span><span class="p">,</span>
<span class="s1">&#39;/2/delete&#39;</span><span class="p">,</span>
<span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_exists_required</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">404</span>
</pre></div>
</div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">create</span></code> and <code class="docutils literal notranslate"><span class="pre">update</span></code> views should render and return a
<code class="docutils literal notranslate"><span class="pre">200</span> <span class="pre">OK</span></code> status for a <code class="docutils literal notranslate"><span class="pre">GET</span></code> request. When valid data is sent in a
<code class="docutils literal notranslate"><span class="pre">POST</span></code> request, <code class="docutils literal notranslate"><span class="pre">create</span></code> should insert the new post data into the
database, and <code class="docutils literal notranslate"><span class="pre">update</span></code> should modify the existing data. Both pages
should show an error message on invalid data.</p>
<div class="literal-block-wrapper docutils container" id="id12">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_blog.py</span></code></span><a class="headerlink" href="#id12" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">test_create</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/create&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
<span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s1">&#39;/create&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;title&#39;</span><span class="p">:</span> <span class="s1">&#39;created&#39;</span><span class="p">,</span> <span class="s1">&#39;body&#39;</span><span class="p">:</span> <span class="s1">&#39;&#39;</span><span class="p">})</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="n">count</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">&#39;SELECT COUNT(id) FROM post&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">assert</span> <span class="n">count</span> <span class="o">==</span> <span class="mi">2</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_update</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;/1/update&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
<span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s1">&#39;/1/update&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;title&#39;</span><span class="p">:</span> <span class="s1">&#39;updated&#39;</span><span class="p">,</span> <span class="s1">&#39;body&#39;</span><span class="p">:</span> <span class="s1">&#39;&#39;</span><span class="p">})</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="n">post</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">&#39;SELECT * FROM post WHERE id = 1&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">post</span><span class="p">[</span><span class="s1">&#39;title&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;updated&#39;</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">parametrize</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">,</span> <span class="p">(</span>
<span class="s1">&#39;/create&#39;</span><span class="p">,</span>
<span class="s1">&#39;/1/update&#39;</span><span class="p">,</span>
<span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">test_create_update_validate</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;title&#39;</span><span class="p">:</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="s1">&#39;body&#39;</span><span class="p">:</span> <span class="s1">&#39;&#39;</span><span class="p">})</span>
<span class="k">assert</span> <span class="sa">b</span><span class="s1">&#39;Title is required.&#39;</span> <span class="ow">in</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span>
</pre></div>
</div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">delete</span></code> view should redirect to the index URL and the post should
no longer exist in the database.</p>
<div class="literal-block-wrapper docutils container" id="id13">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">tests/test_blog.py</span></code></span><a class="headerlink" href="#id13" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">test_delete</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">auth</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
<span class="n">auth</span><span class="o">.</span><span class="n">login</span><span class="p">()</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s1">&#39;/1/delete&#39;</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Location&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;/&quot;</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="n">post</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">&#39;SELECT * FROM post WHERE id = 1&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">post</span> <span class="ow">is</span> <span class="kc">None</span>
</pre></div>
</div>
</div>
</section>
<section id="running-the-tests">
<h2>Running the Tests<a class="headerlink" href="#running-the-tests" title="Link to this heading"></a></h2>
<p>Some extra configuration, which is not required but makes running tests with coverage
less verbose, can be added to the projects <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file.</p>
<div class="literal-block-wrapper docutils container" id="id14">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code></span><a class="headerlink" href="#id14" title="Link to this code"></a></div>
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[tool.pytest.ini_options]</span>
<span class="n">testpaths</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;tests&quot;</span><span class="p">]</span>
<span class="k">[tool.coverage.run]</span>
<span class="n">branch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span>
<span class="n">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;flaskr&quot;</span><span class="p">]</span>
</pre></div>
</div>
</div>
<p>To run the tests, use the <code class="docutils literal notranslate"><span class="pre">pytest</span></code> command. It will find and run all
the test functions youve written.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pytest
========================= test session starts ==========================
platform linux -- Python 3.6.4, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
rootdir: /home/user/Projects/flask-tutorial
collected 23 items
tests/test_auth.py ........ [ 34%]
tests/test_blog.py ............ [ 86%]
tests/test_db.py .. [ 95%]
tests/test_factory.py .. [100%]
====================== 24 passed in 0.64 seconds =======================
</pre></div>
</div>
<p>If any tests fail, pytest will show the error that was raised. You can
run <code class="docutils literal notranslate"><span class="pre">pytest</span> <span class="pre">-v</span></code> to get a list of each test function rather than dots.</p>
<p>To measure the code coverage of your tests, use the <code class="docutils literal notranslate"><span class="pre">coverage</span></code> command
to run pytest instead of running it directly.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ coverage run -m pytest
</pre></div>
</div>
<p>You can either view a simple coverage report in the terminal:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ coverage report
Name Stmts Miss Branch BrPart Cover
------------------------------------------------------
flaskr/__init__.py 21 0 2 0 100%
flaskr/auth.py 54 0 22 0 100%
flaskr/blog.py 54 0 16 0 100%
flaskr/db.py 24 0 4 0 100%
------------------------------------------------------
TOTAL 153 0 44 0 100%
</pre></div>
</div>
<p>An HTML report allows you to see which lines were covered in each file:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ coverage html
</pre></div>
</div>
<p>This generates files in the <code class="docutils literal notranslate"><span class="pre">htmlcov</span></code> directory. Open
<code class="docutils literal notranslate"><span class="pre">htmlcov/index.html</span></code> in your browser to see the report.</p>
<p>Continue to <a class="reference internal" href="deploy.html"><span class="doc">Deploy to Production</span></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="#">Test Coverage</a><ul>
<li><a class="reference internal" href="#setup-and-fixtures">Setup and Fixtures</a></li>
<li><a class="reference internal" href="#factory">Factory</a></li>
<li><a class="reference internal" href="#database">Database</a></li>
<li><a class="reference internal" href="#authentication">Authentication</a></li>
<li><a class="reference internal" href="#blog">Blog</a></li>
<li><a class="reference internal" href="#running-the-tests">Running the Tests</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="install.html" title="previous chapter">Make the Project Installable</a>
<li>Next: <a href="deploy.html" title="next chapter">Deploy to Production</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,383 @@
<!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>Blueprints and Views &#8212; 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="Templates" href="templates.html" />
<link rel="prev" title="Define and Access the Database" href="database.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="templates.html" title="Templates"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="database.html" title="Define and Access the Database"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Blueprints and Views</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="blueprints-and-views">
<h1>Blueprints and Views<a class="headerlink" href="#blueprints-and-views" title="Link to this heading"></a></h1>
<p>A view function is the code you write to respond to requests to your
application. Flask uses patterns to match the incoming request URL to
the view that should handle it. The view returns data that Flask turns
into an outgoing response. Flask can also go the other direction and
generate a URL to a view based on its name and arguments.</p>
<section id="create-a-blueprint">
<h2>Create a Blueprint<a class="headerlink" href="#create-a-blueprint" title="Link to this heading"></a></h2>
<p>A <a class="reference internal" href="../api.html#flask.Blueprint" title="flask.Blueprint"><code class="xref py py-class docutils literal notranslate"><span class="pre">Blueprint</span></code></a> is a way to organize a group of related views and
other code. Rather than registering views and other code directly with
an application, they are registered with a blueprint. Then the blueprint
is registered with the application when it is available in the factory
function.</p>
<p>Flaskr will have two blueprints, one for authentication functions and
one for the blog posts functions. The code for each blueprint will go
in a separate module. Since the blog needs to know about authentication,
youll write the authentication one first.</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/auth.py</span></code></span><a class="headerlink" href="#id1" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">functools</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flask</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
<span class="n">Blueprint</span><span class="p">,</span> <span class="n">flash</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">session</span><span class="p">,</span> <span class="n">url_for</span>
<span class="p">)</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">werkzeug.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">check_password_hash</span><span class="p">,</span> <span class="n">generate_password_hash</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">flaskr.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_db</span>
<span class="n">bp</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s1">&#39;auth&#39;</span><span class="p">,</span> <span class="vm">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s1">&#39;/auth&#39;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>This creates a <a class="reference internal" href="../api.html#flask.Blueprint" title="flask.Blueprint"><code class="xref py py-class docutils literal notranslate"><span class="pre">Blueprint</span></code></a> named <code class="docutils literal notranslate"><span class="pre">'auth'</span></code>. Like the application
object, the blueprint needs to know where its defined, so <code class="docutils literal notranslate"><span class="pre">__name__</span></code>
is passed as the second argument. The <code class="docutils literal notranslate"><span class="pre">url_prefix</span></code> will be prepended
to all the URLs associated with the blueprint.</p>
<p>Import and register the blueprint from the factory using
<a class="reference internal" href="../api.html#flask.Flask.register_blueprint" title="flask.Flask.register_blueprint"><code class="xref py py-meth docutils literal notranslate"><span class="pre">app.register_blueprint()</span></code></a>. Place the
new code at the end of the factory function before returning the app.</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/__init__.py</span></code></span><a class="headerlink" href="#id2" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">create_app</span><span class="p">():</span>
<span class="n">app</span> <span class="o">=</span> <span class="o">...</span>
<span class="c1"># existing code omitted</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.</span><span class="w"> </span><span class="kn">import</span> <span class="n">auth</span>
<span class="n">app</span><span class="o">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">auth</span><span class="o">.</span><span class="n">bp</span><span class="p">)</span>
<span class="k">return</span> <span class="n">app</span>
</pre></div>
</div>
</div>
<p>The authentication blueprint will have views to register new users and
to log in and log out.</p>
</section>
<section id="the-first-view-register">
<h2>The First View: Register<a class="headerlink" href="#the-first-view-register" title="Link to this heading"></a></h2>
<p>When the user visits the <code class="docutils literal notranslate"><span class="pre">/auth/register</span></code> URL, the <code class="docutils literal notranslate"><span class="pre">register</span></code> view
will return <a class="reference external" href="https://developer.mozilla.org/docs/Web/HTML">HTML</a> with a form for them to fill out. When they submit
the form, it will validate their input and either show the form again
with an error message or create the new user and go to the login page.</p>
<p>For now you will just write the view code. On the next page, youll
write templates to generate the HTML form.</p>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/auth.py</span></code></span><a class="headerlink" href="#id3" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@bp</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/register&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;GET&#39;</span><span class="p">,</span> <span class="s1">&#39;POST&#39;</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="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">&#39;POST&#39;</span><span class="p">:</span>
<span class="n">username</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">&#39;username&#39;</span><span class="p">]</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">]</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="n">error</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">username</span><span class="p">:</span>
<span class="n">error</span> <span class="o">=</span> <span class="s1">&#39;Username is required.&#39;</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">password</span><span class="p">:</span>
<span class="n">error</span> <span class="o">=</span> <span class="s1">&#39;Password is required.&#39;</span>
<span class="k">if</span> <span class="n">error</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="s2">&quot;INSERT INTO user (username, password) VALUES (?, ?)&quot;</span><span class="p">,</span>
<span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">generate_password_hash</span><span class="p">(</span><span class="n">password</span><span class="p">)),</span>
<span class="p">)</span>
<span class="n">db</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="k">except</span> <span class="n">db</span><span class="o">.</span><span class="n">IntegrityError</span><span class="p">:</span>
<span class="n">error</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;User </span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="s2"> is already registered.&quot;</span>
<span class="k">else</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="s2">&quot;auth.login&quot;</span><span class="p">))</span>
<span class="n">flash</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">&#39;auth/register.html&#39;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Heres what the <code class="docutils literal notranslate"><span class="pre">register</span></code> view function is doing:</p>
<ol class="arabic simple">
<li><p><a class="reference internal" href="../api.html#flask.Blueprint.route" title="flask.Blueprint.route"><code class="xref py py-meth docutils literal notranslate"><span class="pre">&#64;bp.route</span></code></a> associates the URL <code class="docutils literal notranslate"><span class="pre">/register</span></code>
with the <code class="docutils literal notranslate"><span class="pre">register</span></code> view function. When Flask receives a request
to <code class="docutils literal notranslate"><span class="pre">/auth/register</span></code>, it will call the <code class="docutils literal notranslate"><span class="pre">register</span></code> view and use
the return value as the response.</p></li>
<li><p>If the user submitted the form,
<a class="reference internal" href="../api.html#flask.Request.method" title="flask.Request.method"><code class="xref py py-attr docutils literal notranslate"><span class="pre">request.method</span></code></a> will be <code class="docutils literal notranslate"><span class="pre">'POST'</span></code>. In this
case, start validating the input.</p></li>
<li><p><a class="reference internal" href="../api.html#flask.Request.form" title="flask.Request.form"><code class="xref py py-attr docutils literal notranslate"><span class="pre">request.form</span></code></a> is a special type of
<a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#dict" title="(in Python v3.13)"><code class="xref py py-class docutils literal notranslate"><span class="pre">dict</span></code></a> mapping submitted form keys and values. The user will
input their <code class="docutils literal notranslate"><span class="pre">username</span></code> and <code class="docutils literal notranslate"><span class="pre">password</span></code>.</p></li>
<li><p>Validate that <code class="docutils literal notranslate"><span class="pre">username</span></code> and <code class="docutils literal notranslate"><span class="pre">password</span></code> are not empty.</p></li>
<li><p>If validation succeeds, insert the new user data into the database.</p>
<ul class="simple">
<li><p><a class="reference external" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.execute" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db.execute</span></code></a> takes a SQL
query with <code class="docutils literal notranslate"><span class="pre">?</span></code> placeholders for any user input, and a tuple of
values to replace the placeholders with. The database library
will take care of escaping the values so you are not vulnerable
to a <em>SQL injection attack</em>.</p></li>
<li><p>For security, passwords should never be stored in the database
directly. Instead,
<a class="reference external" href="https://werkzeug.palletsprojects.com/en/stable/utils/#werkzeug.security.generate_password_hash" title="(in Werkzeug v3.1.x)"><code class="xref py py-func docutils literal notranslate"><span class="pre">generate_password_hash()</span></code></a> is used to
securely hash the password, and that hash is stored. Since this
query modifies data,
<a class="reference external" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.commit" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db.commit()</span></code></a> needs to be
called afterwards to save the changes.</p></li>
<li><p>An <a class="reference external" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.IntegrityError" title="(in Python v3.13)"><code class="xref py py-exc docutils literal notranslate"><span class="pre">sqlite3.IntegrityError</span></code></a> will occur if the username
already exists, which should be shown to the user as another
validation error.</p></li>
</ul>
</li>
<li><p>After storing the user, they are redirected to the login page.
<a class="reference internal" href="../api.html#flask.url_for" title="flask.url_for"><code class="xref py py-func docutils literal notranslate"><span class="pre">url_for()</span></code></a> generates the URL for the login view based on its
name. This is preferable to writing the URL directly as it allows
you to change the URL later without changing all code that links to
it. <a class="reference internal" href="../api.html#flask.redirect" title="flask.redirect"><code class="xref py py-func docutils literal notranslate"><span class="pre">redirect()</span></code></a> generates a redirect response to the generated
URL.</p></li>
<li><p>If validation fails, the error is shown to the user. <a class="reference internal" href="../api.html#flask.flash" title="flask.flash"><code class="xref py py-func docutils literal notranslate"><span class="pre">flash()</span></code></a>
stores messages that can be retrieved when rendering the template.</p></li>
<li><p>When the user initially navigates to <code class="docutils literal notranslate"><span class="pre">auth/register</span></code>, or
there was a validation error, an HTML page with the registration
form should be shown. <a class="reference internal" href="../api.html#flask.render_template" title="flask.render_template"><code class="xref py py-func docutils literal notranslate"><span class="pre">render_template()</span></code></a> will render a template
containing the HTML, which youll write in the next step of the
tutorial.</p></li>
</ol>
</section>
<section id="login">
<h2>Login<a class="headerlink" href="#login" title="Link to this heading"></a></h2>
<p>This view follows the same pattern as the <code class="docutils literal notranslate"><span class="pre">register</span></code> view above.</p>
<div class="literal-block-wrapper docutils container" id="id4">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/auth.py</span></code></span><a class="headerlink" href="#id4" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@bp</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;GET&#39;</span><span class="p">,</span> <span class="s1">&#39;POST&#39;</span><span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">login</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">&#39;POST&#39;</span><span class="p">:</span>
<span class="n">username</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">&#39;username&#39;</span><span class="p">]</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">]</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
<span class="n">error</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="s1">&#39;SELECT * FROM user WHERE username = ?&#39;</span><span class="p">,</span> <span class="p">(</span><span class="n">username</span><span class="p">,)</span>
<span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
<span class="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">error</span> <span class="o">=</span> <span class="s1">&#39;Incorrect username.&#39;</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">check_password_hash</span><span class="p">(</span><span class="n">user</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">],</span> <span class="n">password</span><span class="p">):</span>
<span class="n">error</span> <span class="o">=</span> <span class="s1">&#39;Incorrect password.&#39;</span>
<span class="k">if</span> <span class="n">error</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">session</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
<span class="n">session</span><span class="p">[</span><span class="s1">&#39;user_id&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">user</span><span class="p">[</span><span class="s1">&#39;id&#39;</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">&#39;index&#39;</span><span class="p">))</span>
<span class="n">flash</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">&#39;auth/login.html&#39;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>There are a few differences from the <code class="docutils literal notranslate"><span class="pre">register</span></code> view:</p>
<ol class="arabic">
<li><p>The user is queried first and stored in a variable for later use.</p>
<p><a class="reference external" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchone" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">fetchone()</span></code></a> returns one row from the query.
If the query returned no results, it returns <code class="docutils literal notranslate"><span class="pre">None</span></code>. Later,
<a class="reference external" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchall" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">fetchall()</span></code></a> will be used, which returns a list
of all results.</p>
</li>
<li><p><a class="reference external" href="https://werkzeug.palletsprojects.com/en/stable/utils/#werkzeug.security.check_password_hash" title="(in Werkzeug v3.1.x)"><code class="xref py py-func docutils literal notranslate"><span class="pre">check_password_hash()</span></code></a> hashes the submitted
password in the same way as the stored hash and securely compares
them. If they match, the password is valid.</p></li>
<li><p><a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a> is a <a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#dict" title="(in Python v3.13)"><code class="xref py py-class docutils literal notranslate"><span class="pre">dict</span></code></a> that stores data across requests.
When validation succeeds, the users <code class="docutils literal notranslate"><span class="pre">id</span></code> is stored in a new
session. The data is stored in a <em>cookie</em> that is sent to the
browser, and the browser then sends it back with subsequent requests.
Flask securely <em>signs</em> the data so that it cant be tampered with.</p></li>
</ol>
<p>Now that the users <code class="docutils literal notranslate"><span class="pre">id</span></code> is stored in the <a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a>, it will be
available on subsequent requests. At the beginning of each request, if
a user is logged in their information should be loaded and made
available to other views.</p>
<div class="literal-block-wrapper docutils container" id="id5">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/auth.py</span></code></span><a class="headerlink" href="#id5" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@bp</span><span class="o">.</span><span class="n">before_app_request</span>
<span class="k">def</span><span class="w"> </span><span class="nf">load_logged_in_user</span><span class="p">():</span>
<span class="n">user_id</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;user_id&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">user_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">g</span><span class="o">.</span><span class="n">user</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">g</span><span class="o">.</span><span class="n">user</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="s1">&#39;SELECT * FROM user WHERE id = ?&#39;</span><span class="p">,</span> <span class="p">(</span><span class="n">user_id</span><span class="p">,)</span>
<span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
</pre></div>
</div>
</div>
<p><a class="reference internal" href="../api.html#flask.Blueprint.before_app_request" title="flask.Blueprint.before_app_request"><code class="xref py py-meth docutils literal notranslate"><span class="pre">bp.before_app_request()</span></code></a> registers
a function that runs before the view function, no matter what URL is
requested. <code class="docutils literal notranslate"><span class="pre">load_logged_in_user</span></code> checks if a user id is stored in the
<a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a> and gets that users data from the database, storing it
on <a class="reference internal" href="../api.html#flask.g" title="flask.g"><code class="xref py py-data docutils literal notranslate"><span class="pre">g.user</span></code></a>, which lasts for the length of the request. If
there is no user id, or if the id doesnt exist, <code class="docutils literal notranslate"><span class="pre">g.user</span></code> will be
<code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
</section>
<section id="logout">
<h2>Logout<a class="headerlink" href="#logout" title="Link to this heading"></a></h2>
<p>To log out, you need to remove the user id from the <a class="reference internal" href="../api.html#flask.session" title="flask.session"><code class="xref py py-data docutils literal notranslate"><span class="pre">session</span></code></a>.
Then <code class="docutils literal notranslate"><span class="pre">load_logged_in_user</span></code> wont load a user on subsequent requests.</p>
<div class="literal-block-wrapper docutils container" id="id6">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/auth.py</span></code></span><a class="headerlink" href="#id6" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@bp</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/logout&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">logout</span><span class="p">():</span>
<span class="n">session</span><span class="o">.</span><span class="n">clear</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">&#39;index&#39;</span><span class="p">))</span>
</pre></div>
</div>
</div>
</section>
<section id="require-authentication-in-other-views">
<h2>Require Authentication in Other Views<a class="headerlink" href="#require-authentication-in-other-views" title="Link to this heading"></a></h2>
<p>Creating, editing, and deleting blog posts will require a user to be
logged in. A <em>decorator</em> can be used to check this for each view its
applied to.</p>
<div class="literal-block-wrapper docutils container" id="id7">
<div class="code-block-caption"><span class="caption-text"><code class="docutils literal notranslate"><span class="pre">flaskr/auth.py</span></code></span><a class="headerlink" href="#id7" title="Link to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">login_required</span><span class="p">(</span><span class="n">view</span><span class="p">):</span>
<span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">view</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">wrapped_view</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">g</span><span class="o">.</span><span class="n">user</span> <span class="ow">is</span> <span class="kc">None</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">&#39;auth.login&#39;</span><span class="p">))</span>
<span class="k">return</span> <span class="n">view</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">wrapped_view</span>
</pre></div>
</div>
</div>
<p>This decorator returns a new view function that wraps the original view
its applied to. The new function checks if a user is loaded and
redirects to the login page otherwise. If a user is loaded the original
view is called and continues normally. Youll use this decorator when
writing the blog views.</p>
</section>
<section id="endpoints-and-urls">
<h2>Endpoints and URLs<a class="headerlink" href="#endpoints-and-urls" title="Link to this heading"></a></h2>
<p>The <a class="reference internal" href="../api.html#flask.url_for" title="flask.url_for"><code class="xref py py-func docutils literal notranslate"><span class="pre">url_for()</span></code></a> function generates the URL to a view based on a name
and arguments. The name associated with a view is also called the
<em>endpoint</em>, and by default its the same as the name of the view
function.</p>
<p>For example, the <code class="docutils literal notranslate"><span class="pre">hello()</span></code> view that was added to the app
factory earlier in the tutorial has the name <code class="docutils literal notranslate"><span class="pre">'hello'</span></code> and can be
linked to with <code class="docutils literal notranslate"><span class="pre">url_for('hello')</span></code>. If it took an argument, which
youll see later, it would be linked to using
<code class="docutils literal notranslate"><span class="pre">url_for('hello',</span> <span class="pre">who='World')</span></code>.</p>
<p>When using a blueprint, the name of the blueprint is prepended to the
name of the function, so the endpoint for the <code class="docutils literal notranslate"><span class="pre">login</span></code> function you
wrote above is <code class="docutils literal notranslate"><span class="pre">'auth.login'</span></code> because you added it to the <code class="docutils literal notranslate"><span class="pre">'auth'</span></code>
blueprint.</p>
<p>Continue to <a class="reference internal" href="templates.html"><span class="doc">Templates</span></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="#">Blueprints and Views</a><ul>
<li><a class="reference internal" href="#create-a-blueprint">Create a Blueprint</a></li>
<li><a class="reference internal" href="#the-first-view-register">The First View: Register</a></li>
<li><a class="reference internal" href="#login">Login</a></li>
<li><a class="reference internal" href="#logout">Logout</a></li>
<li><a class="reference internal" href="#require-authentication-in-other-views">Require Authentication in Other Views</a></li>
<li><a class="reference internal" href="#endpoints-and-urls">Endpoints and URLs</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Tutorial</a>
<ul>
<li>Previous: <a href="database.html" title="previous chapter">Define and Access the Database</a>
<li>Next: <a href="templates.html" title="next chapter">Templates</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,416 @@
<!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>Class-based Views &#8212; 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="Application Structure and Lifecycle" href="lifecycle.html" />
<link rel="prev" title="Signals" href="signals.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="lifecycle.html" title="Application Structure and Lifecycle"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="signals.html" title="Signals"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Class-based Views</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="class-based-views">
<h1>Class-based Views<a class="headerlink" href="#class-based-views" title="Link to this heading"></a></h1>
<p>This page introduces using the <a class="reference internal" href="api.html#flask.views.View" title="flask.views.View"><code class="xref py py-class docutils literal notranslate"><span class="pre">View</span></code></a> and <a class="reference internal" href="api.html#flask.views.MethodView" title="flask.views.MethodView"><code class="xref py py-class docutils literal notranslate"><span class="pre">MethodView</span></code></a>
classes to write class-based views.</p>
<p>A class-based view is a class that acts as a view function. Because it
is a class, different instances of the class can be created with
different arguments, to change the behavior of the view. This is also
known as generic, reusable, or pluggable views.</p>
<p>An example of where this is useful is defining a class that creates an
API based on the database model it is initialized with.</p>
<p>For more complex API behavior and customization, look into the various
API extensions for Flask.</p>
<section id="basic-reusable-view">
<h2>Basic Reusable View<a class="headerlink" href="#basic-reusable-view" title="Link to this heading"></a></h2>
<p>Lets walk through an example converting a view function to a view
class. We start with a view function that queries a list of users then
renders a template to show the list.</p>
<div class="highlight-python 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="s2">&quot;/users/&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">user_list</span><span class="p">():</span>
<span class="n">users</span> <span class="o">=</span> <span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s2">&quot;users.html&quot;</span><span class="p">,</span> <span class="n">users</span><span class="o">=</span><span class="n">users</span><span class="p">)</span>
</pre></div>
</div>
<p>This works for the user model, but lets say you also had more models
that needed list pages. Youd need to write another view function for
each model, even though the only thing that would change is the model
and template name.</p>
<p>Instead, you can write a <a class="reference internal" href="api.html#flask.views.View" title="flask.views.View"><code class="xref py py-class docutils literal notranslate"><span class="pre">View</span></code></a> subclass that will query a model
and render a template. As the first step, well convert the view to a
class without any customization.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">flask.views</span><span class="w"> </span><span class="kn">import</span> <span class="n">View</span>
<span class="k">class</span><span class="w"> </span><span class="nc">UserList</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">users</span> <span class="o">=</span> <span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s2">&quot;users.html&quot;</span><span class="p">,</span> <span class="n">objects</span><span class="o">=</span><span class="n">users</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s2">&quot;/users/&quot;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">UserList</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s2">&quot;user_list&quot;</span><span class="p">))</span>
</pre></div>
</div>
<p>The <a class="reference internal" href="api.html#flask.views.View.dispatch_request" title="flask.views.View.dispatch_request"><code class="xref py py-meth docutils literal notranslate"><span class="pre">View.dispatch_request()</span></code></a> method is the equivalent of the view
function. Calling <a class="reference internal" href="api.html#flask.views.View.as_view" title="flask.views.View.as_view"><code class="xref py py-meth docutils literal notranslate"><span class="pre">View.as_view()</span></code></a> method will create a view
function that can be registered on the app with its
<a class="reference internal" href="api.html#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><code class="xref py py-meth docutils literal notranslate"><span class="pre">add_url_rule()</span></code></a> method. The first argument to
<code class="docutils literal notranslate"><span class="pre">as_view</span></code> is the name to use to refer to the view with
<a class="reference internal" href="api.html#flask.url_for" title="flask.url_for"><code class="xref py py-func docutils literal notranslate"><span class="pre">url_for()</span></code></a>.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>You cant decorate the class with <code class="docutils literal notranslate"><span class="pre">&#64;app.route()</span></code> the way youd
do with a basic view function.</p>
</div>
<p>Next, we need to be able to register the same view class for different
models and templates, to make it more useful than the original function.
The class will take two arguments, the model and template, and store
them on <code class="docutils literal notranslate"><span class="pre">self</span></code>. Then <code class="docutils literal notranslate"><span class="pre">dispatch_request</span></code> can reference these instead
of hard-coded values.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">ListView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">,</span> <span class="n">template</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span>
<span class="bp">self</span><span class="o">.</span><span class="n">template</span> <span class="o">=</span> <span class="n">template</span>
<span class="k">def</span><span class="w"> </span><span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">items</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">template</span><span class="p">,</span> <span class="n">items</span><span class="o">=</span><span class="n">items</span><span class="p">)</span>
</pre></div>
</div>
<p>Remember, we create the view function with <code class="docutils literal notranslate"><span class="pre">View.as_view()</span></code> instead of
creating the class directly. Any extra arguments passed to <code class="docutils literal notranslate"><span class="pre">as_view</span></code>
are then passed when creating the class. Now we can register the same
view to handle multiple models.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span>
<span class="s2">&quot;/users/&quot;</span><span class="p">,</span>
<span class="n">view_func</span><span class="o">=</span><span class="n">ListView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s2">&quot;user_list&quot;</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="s2">&quot;users.html&quot;</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span>
<span class="s2">&quot;/stories/&quot;</span><span class="p">,</span>
<span class="n">view_func</span><span class="o">=</span><span class="n">ListView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s2">&quot;story_list&quot;</span><span class="p">,</span> <span class="n">Story</span><span class="p">,</span> <span class="s2">&quot;stories.html&quot;</span><span class="p">),</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="url-variables">
<h2>URL Variables<a class="headerlink" href="#url-variables" title="Link to this heading"></a></h2>
<p>Any variables captured by the URL are passed as keyword arguments to the
<code class="docutils literal notranslate"><span class="pre">dispatch_request</span></code> method, as they would be for a regular view
function.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">DetailView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span>
<span class="bp">self</span><span class="o">.</span><span class="n">template</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="vm">__name__</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="si">}</span><span class="s2">/detail.html&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">get_or_404</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">template</span><span class="p">,</span> <span class="n">item</span><span class="o">=</span><span class="n">item</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span>
<span class="s2">&quot;/users/&lt;int:id&gt;&quot;</span><span class="p">,</span>
<span class="n">view_func</span><span class="o">=</span><span class="n">DetailView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s2">&quot;user_detail&quot;</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="view-lifetime-and-self">
<h2>View Lifetime and <code class="docutils literal notranslate"><span class="pre">self</span></code><a class="headerlink" href="#view-lifetime-and-self" title="Link to this heading"></a></h2>
<p>By default, a new instance of the view class is created every time a
request is handled. This means that it is safe to write other data to
<code class="docutils literal notranslate"><span class="pre">self</span></code> during the request, since the next request will not see it,
unlike other forms of global state.</p>
<p>However, if your view class needs to do a lot of complex initialization,
doing it for every request is unnecessary and can be inefficient. To
avoid this, set <a class="reference internal" href="api.html#flask.views.View.init_every_request" title="flask.views.View.init_every_request"><code class="xref py py-attr docutils literal notranslate"><span class="pre">View.init_every_request</span></code></a> to <code class="docutils literal notranslate"><span class="pre">False</span></code>, which will
only create one instance of the class and use it for every request. In
this case, writing to <code class="docutils literal notranslate"><span class="pre">self</span></code> is not safe. If you need to store data
during the request, use <a class="reference internal" href="api.html#flask.g" title="flask.g"><code class="xref py py-data docutils literal notranslate"><span class="pre">g</span></code></a> instead.</p>
<p>In the <code class="docutils literal notranslate"><span class="pre">ListView</span></code> example, nothing writes to <code class="docutils literal notranslate"><span class="pre">self</span></code> during the
request, so it is more efficient to create a single instance.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">ListView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
<span class="n">init_every_request</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">,</span> <span class="n">template</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span>
<span class="bp">self</span><span class="o">.</span><span class="n">template</span> <span class="o">=</span> <span class="n">template</span>
<span class="k">def</span><span class="w"> </span><span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">items</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">template</span><span class="p">,</span> <span class="n">items</span><span class="o">=</span><span class="n">items</span><span class="p">)</span>
</pre></div>
</div>
<p>Different instances will still be created each for each <code class="docutils literal notranslate"><span class="pre">as_view</span></code>
call, but not for each request to those views.</p>
</section>
<section id="view-decorators">
<h2>View Decorators<a class="headerlink" href="#view-decorators" title="Link to this heading"></a></h2>
<p>The view class itself is not the view function. View decorators need to
be applied to the view function returned by <code class="docutils literal notranslate"><span class="pre">as_view</span></code>, not the class
itself. Set <a class="reference internal" href="api.html#flask.views.View.decorators" title="flask.views.View.decorators"><code class="xref py py-attr docutils literal notranslate"><span class="pre">View.decorators</span></code></a> to a list of decorators to apply.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">UserList</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
<span class="n">decorators</span> <span class="o">=</span> <span class="p">[</span><span class="n">cache</span><span class="p">(</span><span class="n">minutes</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span> <span class="n">login_required</span><span class="p">]</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s1">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">UserList</span><span class="o">.</span><span class="n">as_view</span><span class="p">())</span>
</pre></div>
</div>
<p>If you didnt set <code class="docutils literal notranslate"><span class="pre">decorators</span></code>, you could apply them manually instead.
This is equivalent to:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">view</span> <span class="o">=</span> <span class="n">UserList</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s2">&quot;users_list&quot;</span><span class="p">)</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">cache</span><span class="p">(</span><span class="n">minutes</span><span class="o">=</span><span class="mi">2</span><span class="p">)(</span><span class="n">view</span><span class="p">)</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">login_required</span><span class="p">(</span><span class="n">view</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s1">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">view</span><span class="p">)</span>
</pre></div>
</div>
<p>Keep in mind that order matters. If youre used to <code class="docutils literal notranslate"><span class="pre">&#64;decorator</span></code> style,
this is equivalent to:</p>
<div class="highlight-python 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="s2">&quot;/users/&quot;</span><span class="p">)</span>
<span class="nd">@login_required</span>
<span class="nd">@cache</span><span class="p">(</span><span class="n">minutes</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">user_list</span><span class="p">():</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
<section id="method-hints">
<h2>Method Hints<a class="headerlink" href="#method-hints" title="Link to this heading"></a></h2>
<p>A common pattern is to register a view with <code class="docutils literal notranslate"><span class="pre">methods=[&quot;GET&quot;,</span> <span class="pre">&quot;POST&quot;]</span></code>,
then check <code class="docutils literal notranslate"><span class="pre">request.method</span> <span class="pre">==</span> <span class="pre">&quot;POST&quot;</span></code> to decide what to do. Setting
<a class="reference internal" href="api.html#flask.views.View.methods" title="flask.views.View.methods"><code class="xref py py-attr docutils literal notranslate"><span class="pre">View.methods</span></code></a> is equivalent to passing the list of methods to
<code class="docutils literal notranslate"><span class="pre">add_url_rule</span></code> or <code class="docutils literal notranslate"><span class="pre">route</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MyView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
<span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;GET&quot;</span><span class="p">,</span> <span class="s2">&quot;POST&quot;</span><span class="p">]</span>
<span class="k">def</span><span class="w"> </span><span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</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="s2">&quot;POST&quot;</span><span class="p">:</span>
<span class="o">...</span>
<span class="o">...</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s1">&#39;/my-view&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">MyView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s1">&#39;my-view&#39;</span><span class="p">))</span>
</pre></div>
</div>
<p>This is equivalent to the following, except further subclasses can
inherit or change the methods.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span>
<span class="s2">&quot;/my-view&quot;</span><span class="p">,</span>
<span class="n">view_func</span><span class="o">=</span><span class="n">MyView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s2">&quot;my-view&quot;</span><span class="p">),</span>
<span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;GET&quot;</span><span class="p">,</span> <span class="s2">&quot;POST&quot;</span><span class="p">],</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="method-dispatching-and-apis">
<h2>Method Dispatching and APIs<a class="headerlink" href="#method-dispatching-and-apis" title="Link to this heading"></a></h2>
<p>For APIs it can be helpful to use a different function for each HTTP
method. <a class="reference internal" href="api.html#flask.views.MethodView" title="flask.views.MethodView"><code class="xref py py-class docutils literal notranslate"><span class="pre">MethodView</span></code></a> extends the basic <a class="reference internal" href="api.html#flask.views.View" title="flask.views.View"><code class="xref py py-class docutils literal notranslate"><span class="pre">View</span></code></a> to dispatch
to different methods of the class based on the request method. Each HTTP
method maps to a method of the class with the same (lowercase) name.</p>
<p><a class="reference internal" href="api.html#flask.views.MethodView" title="flask.views.MethodView"><code class="xref py py-class docutils literal notranslate"><span class="pre">MethodView</span></code></a> automatically sets <a class="reference internal" href="api.html#flask.views.View.methods" title="flask.views.View.methods"><code class="xref py py-attr docutils literal notranslate"><span class="pre">View.methods</span></code></a> based on the
methods defined by the class. It even knows how to handle subclasses
that override or define other methods.</p>
<p>We can make a generic <code class="docutils literal notranslate"><span class="pre">ItemAPI</span></code> class that provides get (detail),
patch (edit), and delete methods for a given model. A <code class="docutils literal notranslate"><span class="pre">GroupAPI</span></code> can
provide get (list) and post (create) methods.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">flask.views</span><span class="w"> </span><span class="kn">import</span> <span class="n">MethodView</span>
<span class="k">class</span><span class="w"> </span><span class="nc">ItemAPI</span><span class="p">(</span><span class="n">MethodView</span><span class="p">):</span>
<span class="n">init_every_request</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validator</span> <span class="o">=</span> <span class="n">generate_validator</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">_get_item</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">get_or_404</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_item</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">to_json</span><span class="p">())</span>
<span class="k">def</span><span class="w"> </span><span class="nf">patch</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_item</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="n">errors</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">validator</span><span class="o">.</span><span class="n">validate</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">json</span><span class="p">)</span>
<span class="k">if</span> <span class="n">errors</span><span class="p">:</span>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">errors</span><span class="p">),</span> <span class="mi">400</span>
<span class="n">item</span><span class="o">.</span><span class="n">update_from_json</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">json</span><span class="p">)</span>
<span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">to_json</span><span class="p">())</span>
<span class="k">def</span><span class="w"> </span><span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_item</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="k">return</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="mi">204</span>
<span class="k">class</span><span class="w"> </span><span class="nc">GroupAPI</span><span class="p">(</span><span class="n">MethodView</span><span class="p">):</span>
<span class="n">init_every_request</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validator</span> <span class="o">=</span> <span class="n">generate_validator</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">items</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">([</span><span class="n">item</span><span class="o">.</span><span class="n">to_json</span><span class="p">()</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">items</span><span class="p">])</span>
<span class="k">def</span><span class="w"> </span><span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">errors</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">validator</span><span class="o">.</span><span class="n">validate</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">json</span><span class="p">)</span>
<span class="k">if</span> <span class="n">errors</span><span class="p">:</span>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">errors</span><span class="p">),</span> <span class="mi">400</span>
<span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">from_json</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">json</span><span class="p">))</span>
<span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">to_json</span><span class="p">())</span>
<span class="k">def</span><span class="w"> </span><span class="nf">register_api</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">model</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">ItemAPI</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">-item&quot;</span><span class="p">,</span> <span class="n">model</span><span class="p">)</span>
<span class="n">group</span> <span class="o">=</span> <span class="n">GroupAPI</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">-group&quot;</span><span class="p">,</span> <span class="n">model</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">/&lt;int:id&gt;&quot;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">item</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">/&quot;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">group</span><span class="p">)</span>
<span class="n">register_api</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="s2">&quot;users&quot;</span><span class="p">)</span>
<span class="n">register_api</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">Story</span><span class="p">,</span> <span class="s2">&quot;stories&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>This produces the following views, a standard REST API!</p>
<table class="docutils align-default">
<tbody>
<tr class="row-odd"><td><p>URL</p></td>
<td><p>Method</p></td>
<td><p>Description</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">/users/</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">GET</span></code></p></td>
<td><p>List all users</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">/users/</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">POST</span></code></p></td>
<td><p>Create a new user</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">/users/&lt;id&gt;</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">GET</span></code></p></td>
<td><p>Show a single user</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">/users/&lt;id&gt;</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">PATCH</span></code></p></td>
<td><p>Update a user</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">/users/&lt;id&gt;</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">DELETE</span></code></p></td>
<td><p>Delete a user</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">/stories/</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">GET</span></code></p></td>
<td><p>List all stories</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">/stories/</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">POST</span></code></p></td>
<td><p>Create a new story</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">/stories/&lt;id&gt;</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">GET</span></code></p></td>
<td><p>Show a single story</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">/stories/&lt;id&gt;</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">PATCH</span></code></p></td>
<td><p>Update a story</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">/stories/&lt;id&gt;</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">DELETE</span></code></p></td>
<td><p>Delete a story</p></td>
</tr>
</tbody>
</table>
</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="#">Class-based Views</a><ul>
<li><a class="reference internal" href="#basic-reusable-view">Basic Reusable View</a></li>
<li><a class="reference internal" href="#url-variables">URL Variables</a></li>
<li><a class="reference internal" href="#view-lifetime-and-self">View Lifetime and <code class="docutils literal notranslate"><span class="pre">self</span></code></a></li>
<li><a class="reference internal" href="#view-decorators">View Decorators</a></li>
<li><a class="reference internal" href="#method-hints">Method Hints</a></li>
<li><a class="reference internal" href="#method-dispatching-and-apis">Method Dispatching and APIs</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="index.html">Overview</a>
<ul>
<li>Previous: <a href="signals.html" title="previous chapter">Signals</a>
<li>Next: <a href="lifecycle.html" title="next chapter">Application Structure and Lifecycle</a>
</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>

View file

@ -0,0 +1,367 @@
<!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>Security Considerations &#8212; 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="Deploying to Production" href="deploying/index.html" />
<link rel="prev" title="Single-Page Applications" href="patterns/singlepageapplications.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="deploying/index.html" title="Deploying to Production"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="patterns/singlepageapplications.html" title="Single-Page Applications"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Flask Documentation (3.2.x)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Security Considerations</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="security-considerations">
<h1>Security Considerations<a class="headerlink" href="#security-considerations" title="Link to this heading"></a></h1>
<p>Web applications face many types of potential security problems, and it can be
hard to get everything right, or even to know what “right” is in general. Flask
tries to solve a few of these things by default, but there are other parts you
may have to take care of yourself. Many of these solutions are tradeoffs, and
will depend on each applications specific needs and threat model. Many hosting
platforms may take care of certain types of problems without the need for the
Flask application to handle them.</p>
<section id="resource-use">
<h2>Resource Use<a class="headerlink" href="#resource-use" title="Link to this heading"></a></h2>
<p>A common category of attacks is “Denial of Service” (DoS or DDoS). This is a
very broad category, and different variants target different layers in a
deployed application. In general, something is done to increase how much
processing time or memory is used to handle each request, to the point where
there are not enough resources to handle legitimate requests.</p>
<p>Flask provides a few configuration options to handle resource use. They can
also be set on individual requests to customize only that request. The
documentation for each goes into more detail.</p>
<ul class="simple">
<li><p><a class="reference internal" href="config.html#MAX_CONTENT_LENGTH" title="MAX_CONTENT_LENGTH"><code class="xref py py-data docutils literal notranslate"><span class="pre">MAX_CONTENT_LENGTH</span></code></a> or <a class="reference internal" href="api.html#flask.Request.max_content_length" title="flask.Request.max_content_length"><code class="xref py py-attr docutils literal notranslate"><span class="pre">Request.max_content_length</span></code></a> controls
how much data will be read from a request. It is not set by default,
although it will still block truly unlimited streams unless the WSGI server
indicates support.</p></li>
<li><p><a class="reference internal" href="config.html#MAX_FORM_MEMORY_SIZE" title="MAX_FORM_MEMORY_SIZE"><code class="xref py py-data docutils literal notranslate"><span class="pre">MAX_FORM_MEMORY_SIZE</span></code></a> or <a class="reference internal" href="api.html#flask.Request.max_form_memory_size" title="flask.Request.max_form_memory_size"><code class="xref py py-attr docutils literal notranslate"><span class="pre">Request.max_form_memory_size</span></code></a>
controls how large any non-file <code class="docutils literal notranslate"><span class="pre">multipart/form-data</span></code> field can be. It is
set to 500kB by default.</p></li>
<li><p><a class="reference internal" href="config.html#MAX_FORM_PARTS" title="MAX_FORM_PARTS"><code class="xref py py-data docutils literal notranslate"><span class="pre">MAX_FORM_PARTS</span></code></a> or <a class="reference internal" href="api.html#flask.Request.max_form_parts" title="flask.Request.max_form_parts"><code class="xref py py-attr docutils literal notranslate"><span class="pre">Request.max_form_parts</span></code></a> controls how many
<code class="docutils literal notranslate"><span class="pre">multipart/form-data</span></code> fields can be parsed. It is set to 1000 by default.
Combined with the default <code class="code docutils literal notranslate"><span class="pre">max_form_memory_size</span></code>, this means that a form
will occupy at most 500MB of memory.</p></li>
</ul>
<p>Regardless of these settings, you should also review what settings are available
from your operating system, container deployment (Docker etc), WSGI server, HTTP
server, and hosting platform. They typically have ways to set process resource
limits, timeouts, and other checks regardless of how Flask is configured.</p>
</section>
<section id="cross-site-scripting-xss">
<span id="security-xss"></span><h2>Cross-Site Scripting (XSS)<a class="headerlink" href="#cross-site-scripting-xss" title="Link to this heading"></a></h2>
<p>Cross site scripting is the concept of injecting arbitrary HTML (and with
it JavaScript) into the context of a website. To remedy this, developers
have to properly escape text so that it cannot include arbitrary HTML
tags. For more information on that have a look at the Wikipedia article
on <a class="reference external" href="https://en.wikipedia.org/wiki/Cross-site_scripting">Cross-Site Scripting</a>.</p>
<p>Flask configures Jinja2 to automatically escape all values unless
explicitly told otherwise. This should rule out all XSS problems caused
in templates, but there are still other places where you have to be
careful:</p>
<ul class="simple">
<li><p>generating HTML without the help of Jinja2</p></li>
<li><p>calling <code class="xref py py-class docutils literal notranslate"><span class="pre">Markup</span></code> on data submitted by users</p></li>
<li><p>sending out HTML from uploaded files, never do that, use the
<code class="docutils literal notranslate"><span class="pre">Content-Disposition:</span> <span class="pre">attachment</span></code> header to prevent that problem.</p></li>
<li><p>sending out textfiles from uploaded files. Some browsers are using
content-type guessing based on the first few bytes so users could
trick a browser to execute HTML.</p></li>
</ul>
<p>Another thing that is very important are unquoted attributes. While
Jinja2 can protect you from XSS issues by escaping HTML, there is one
thing it cannot protect you from: XSS by attribute injection. To counter
this possible attack vector, be sure to always quote your attributes with
either double or single quotes when using Jinja expressions in them:</p>
<div class="highlight-html+jinja notranslate"><div class="highlight"><pre><span></span><span class="p">&lt;</span><span class="nt">input</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">value</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span>
</pre></div>
</div>
<p>Why is this necessary? Because if you would not be doing that, an
attacker could easily inject custom JavaScript handlers. For example an
attacker could inject this piece of HTML+JavaScript:</p>
<div class="highlight-html notranslate"><div class="highlight"><pre><span></span>onmouseover=alert(document.cookie)
</pre></div>
</div>
<p>When the user would then move with the mouse over the input, the cookie
would be presented to the user in an alert window. But instead of showing
the cookie to the user, a good attacker might also execute any other
JavaScript code. In combination with CSS injections the attacker might
even make the element fill out the entire page so that the user would
just have to have the mouse anywhere on the page to trigger the attack.</p>
<p>There is one class of XSS issues that Jinjas escaping does not protect
against. The <code class="docutils literal notranslate"><span class="pre">a</span></code> tags <code class="docutils literal notranslate"><span class="pre">href</span></code> attribute can contain a <code class="code docutils literal notranslate"><span class="pre">javascript:</span></code> URI,
which the browser will execute when clicked if not secured properly.</p>
<div class="highlight-html notranslate"><div class="highlight"><pre><span></span><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;{{ value }}&quot;</span><span class="p">&gt;</span>click here<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;javascript:alert(&#39;unsafe&#39;);&quot;</span><span class="p">&gt;</span>click here<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</pre></div>
</div>
<p>To prevent this, youll need to set the <a class="reference internal" href="#security-csp"><span class="std std-ref">Content Security Policy (CSP)</span></a> response header.</p>
</section>
<section id="cross-site-request-forgery-csrf">
<h2>Cross-Site Request Forgery (CSRF)<a class="headerlink" href="#cross-site-request-forgery-csrf" title="Link to this heading"></a></h2>
<p>Another big problem is CSRF. This is a very complex topic and I wont
outline it here in detail just mention what it is and how to theoretically
prevent it.</p>
<p>If your authentication information is stored in cookies, you have implicit
state management. The state of “being logged in” is controlled by a
cookie, and that cookie is sent with each request to a page.
Unfortunately that includes requests triggered by 3rd party sites. If you
dont keep that in mind, some people might be able to trick your
applications users with social engineering to do stupid things without
them knowing.</p>
<p>Say you have a specific URL that, when you sent <code class="docutils literal notranslate"><span class="pre">POST</span></code> requests to will
delete a users profile (say <code class="docutils literal notranslate"><span class="pre">http://example.com/user/delete</span></code>). If an
attacker now creates a page that sends a post request to that page with
some JavaScript they just have to trick some users to load that page and
their profiles will end up being deleted.</p>
<p>Imagine you were to run Facebook with millions of concurrent users and
someone would send out links to images of little kittens. When users
would go to that page, their profiles would get deleted while they are
looking at images of fluffy cats.</p>
<p>How can you prevent that? Basically for each request that modifies
content on the server you would have to either use a one-time token and
store that in the cookie <strong>and</strong> also transmit it with the form data.
After receiving the data on the server again, you would then have to
compare the two tokens and ensure they are equal.</p>
<p>Why does Flask not do that for you? The ideal place for this to happen is
the form validation framework, which does not exist in Flask.</p>
</section>
<section id="json-security">
<span id="security-json"></span><h2>JSON Security<a class="headerlink" href="#json-security" title="Link to this heading"></a></h2>
<p>In Flask 0.10 and lower, <code class="xref py py-func docutils literal notranslate"><span class="pre">jsonify()</span></code> did not serialize top-level
arrays to JSON. This was because of a security vulnerability in ECMAScript 4.</p>
<p>ECMAScript 5 closed this vulnerability, so only extremely old browsers are
still vulnerable. All of these browsers have <a class="reference external" href="https://github.com/pallets/flask/issues/248#issuecomment-59934857">other more serious
vulnerabilities</a>, so
this behavior was changed and <code class="xref py py-func docutils literal notranslate"><span class="pre">jsonify()</span></code> now supports serializing
arrays.</p>
</section>
<section id="security-headers">
<h2>Security Headers<a class="headerlink" href="#security-headers" title="Link to this heading"></a></h2>
<p>Browsers recognize various response headers in order to control security. We
recommend reviewing each of the headers below for use in your application.
The <a class="reference external" href="https://github.com/GoogleCloudPlatform/flask-talisman">Flask-Talisman</a> extension can be used to manage HTTPS and the security
headers for you.</p>
<section id="http-strict-transport-security-hsts">
<h3>HTTP Strict Transport Security (HSTS)<a class="headerlink" href="#http-strict-transport-security-hsts" title="Link to this heading"></a></h3>
<p>Tells the browser to convert all HTTP requests to HTTPS, preventing
man-in-the-middle (MITM) attacks.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s1">&#39;Strict-Transport-Security&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;max-age=31536000; includeSubDomains&#39;</span>
</pre></div>
</div>
<ul class="simple">
<li><p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security</a></p></li>
</ul>
</section>
<section id="content-security-policy-csp">
<span id="security-csp"></span><h3>Content Security Policy (CSP)<a class="headerlink" href="#content-security-policy-csp" title="Link to this heading"></a></h3>
<p>Tell the browser where it can load various types of resource from. This header
should be used whenever possible, but requires some work to define the correct
policy for your site. A very strict policy would be:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s1">&#39;Content-Security-Policy&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;default-src &#39;self&#39;&quot;</span>
</pre></div>
</div>
<ul class="simple">
<li><p><a class="reference external" href="https://csp.withgoogle.com/docs/index.html">https://csp.withgoogle.com/docs/index.html</a></p></li>
<li><p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy</a></p></li>
</ul>
</section>
<section id="x-content-type-options">
<h3>X-Content-Type-Options<a class="headerlink" href="#x-content-type-options" title="Link to this heading"></a></h3>
<p>Forces the browser to honor the response content type instead of trying to
detect it, which can be abused to generate a cross-site scripting (XSS)
attack.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s1">&#39;X-Content-Type-Options&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;nosniff&#39;</span>
</pre></div>
</div>
<ul class="simple">
<li><p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options</a></p></li>
</ul>
</section>
<section id="x-frame-options">
<h3>X-Frame-Options<a class="headerlink" href="#x-frame-options" title="Link to this heading"></a></h3>
<p>Prevents external sites from embedding your site in an <code class="docutils literal notranslate"><span class="pre">iframe</span></code>. This
prevents a class of attacks where clicks in the outer frame can be translated
invisibly to clicks on your pages elements. This is also known as
“clickjacking”.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s1">&#39;X-Frame-Options&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;SAMEORIGIN&#39;</span>
</pre></div>
</div>
<ul class="simple">
<li><p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options</a></p></li>
</ul>
</section>
<section id="set-cookie-options">
<span id="security-cookie"></span><h3>Set-Cookie options<a class="headerlink" href="#set-cookie-options" title="Link to this heading"></a></h3>
<p>These options can be added to a <code class="docutils literal notranslate"><span class="pre">Set-Cookie</span></code> header to improve their
security. Flask has configuration options to set these on the session cookie.
They can be set on other cookies too.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">Secure</span></code> limits cookies to HTTPS traffic only.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">HttpOnly</span></code> protects the contents of cookies from being read with
JavaScript.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">SameSite</span></code> restricts how cookies are sent with requests from
external sites. Can be set to <code class="docutils literal notranslate"><span class="pre">'Lax'</span></code> (recommended) or <code class="docutils literal notranslate"><span class="pre">'Strict'</span></code>.
<code class="docutils literal notranslate"><span class="pre">Lax</span></code> prevents sending cookies with CSRF-prone requests from
external sites, such as submitting a form. <code class="docutils literal notranslate"><span class="pre">Strict</span></code> prevents sending
cookies with all external requests, including following regular links.</p></li>
</ul>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
<span class="n">SESSION_COOKIE_SECURE</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">SESSION_COOKIE_HTTPONLY</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">SESSION_COOKIE_SAMESITE</span><span class="o">=</span><span class="s1">&#39;Lax&#39;</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">response</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;flask&#39;</span><span class="p">,</span> <span class="n">secure</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">httponly</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">samesite</span><span class="o">=</span><span class="s1">&#39;Lax&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>Specifying <code class="docutils literal notranslate"><span class="pre">Expires</span></code> or <code class="docutils literal notranslate"><span class="pre">Max-Age</span></code> options, will remove the cookie after
the given time, or the current time plus the age, respectively. If neither
option is set, the cookie will be removed when the browser is closed.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># cookie expires after 10 minutes</span>
<span class="n">response</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="s1">&#39;snakes&#39;</span><span class="p">,</span> <span class="s1">&#39;3&#39;</span><span class="p">,</span> <span class="n">max_age</span><span class="o">=</span><span class="mi">600</span><span class="p">)</span>
</pre></div>
</div>
<p>For the session cookie, if <a class="reference internal" href="api.html#flask.session.permanent" title="flask.session.permanent"><code class="xref py py-attr docutils literal notranslate"><span class="pre">session.permanent</span></code></a>
is set, then <a class="reference internal" href="config.html#PERMANENT_SESSION_LIFETIME" title="PERMANENT_SESSION_LIFETIME"><code class="xref py py-data docutils literal notranslate"><span class="pre">PERMANENT_SESSION_LIFETIME</span></code></a> is used to set the expiration.
Flasks default cookie implementation validates that the cryptographic
signature is not older than this value. Lowering this value may help mitigate
replay attacks, where intercepted cookies can be sent at a later time.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
<span class="n">PERMANENT_SESSION_LIFETIME</span><span class="o">=</span><span class="mi">600</span>
<span class="p">)</span>
<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;POST&#39;</span><span class="p">])</span>
<span class="k">def</span><span class="w"> </span><span class="nf">login</span><span class="p">():</span>
<span class="o">...</span>
<span class="n">session</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
<span class="n">session</span><span class="p">[</span><span class="s1">&#39;user_id&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
<span class="n">session</span><span class="o">.</span><span class="n">permanent</span> <span class="o">=</span> <span class="kc">True</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Use <code class="xref py py-class docutils literal notranslate"><span class="pre">itsdangerous.TimedSerializer</span></code> to sign and validate other cookie
values (or any values that need secure signatures).</p>
<ul class="simple">
<li><p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies">https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies</a></p></li>
<li><p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie</a></p></li>
</ul>
</section>
</section>
<section id="copy-paste-to-terminal">
<h2>Copy/Paste to Terminal<a class="headerlink" href="#copy-paste-to-terminal" title="Link to this heading"></a></h2>
<p>Hidden characters such as the backspace character (<code class="docutils literal notranslate"><span class="pre">\b</span></code>, <code class="docutils literal notranslate"><span class="pre">^H</span></code>) can
cause text to render differently in HTML than how it is interpreted if
<a class="reference external" href="https://security.stackexchange.com/q/39118">pasted into a terminal</a>.</p>
<p>For example, <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">y\bose\bm\bi\bt\be\b</span></code> renders as
<code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">yosemite</span></code> in HTML, but the backspaces are applied when pasted
into a terminal, and it becomes <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">os</span></code>.</p>
<p>If you expect users to copy and paste untrusted code from your site,
such as from comments posted by users on a technical blog, consider
applying extra filtering, such as replacing all <code class="docutils literal notranslate"><span class="pre">\b</span></code> characters.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">body</span> <span class="o">=</span> <span class="n">body</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\b</span><span class="s2">&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Most modern terminals will warn about and remove hidden characters when
pasting, so this isnt strictly necessary. Its also possible to craft
dangerous commands in other ways that arent possible to filter.
Depending on your sites use case, it may be good to show a warning
about copying code in general.</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="#">Security Considerations</a><ul>
<li><a class="reference internal" href="#resource-use">Resource Use</a></li>
<li><a class="reference internal" href="#cross-site-scripting-xss">Cross-Site Scripting (XSS)</a></li>
<li><a class="reference internal" href="#cross-site-request-forgery-csrf">Cross-Site Request Forgery (CSRF)</a></li>
<li><a class="reference internal" href="#json-security">JSON Security</a></li>
<li><a class="reference internal" href="#security-headers">Security Headers</a><ul>
<li><a class="reference internal" href="#http-strict-transport-security-hsts">HTTP Strict Transport Security (HSTS)</a></li>
<li><a class="reference internal" href="#content-security-policy-csp">Content Security Policy (CSP)</a></li>
<li><a class="reference internal" href="#x-content-type-options">X-Content-Type-Options</a></li>
<li><a class="reference internal" href="#x-frame-options">X-Frame-Options</a></li>
<li><a class="reference internal" href="#set-cookie-options">Set-Cookie options</a></li>
</ul>
</li>
<li><a class="reference internal" href="#copy-paste-to-terminal">Copy/Paste to Terminal</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="index.html">Overview</a>
<ul>
<li>Previous: <a href="patterns/singlepageapplications.html" title="previous chapter">Single-Page Applications</a>
<li>Next: <a href="deploying/index.html" title="next chapter">Deploying to Production</a>
</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">
&#169; Copyright 2010 Pallets.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
</div>
</body>
</html>