flask/flask-docs/patterns/subclassing.html
2025-04-11 03:04:22 +00:00

269 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>View Decorators &#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="Form Validation with WTForms" href="wtforms.html" />
<link rel="prev" title="Caching" href="caching.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="wtforms.html" title="Form Validation with WTForms"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="caching.html" title="Caching"
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">Patterns for Flask</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">View Decorators</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="view-decorators">
<h1>View Decorators<a class="headerlink" href="#view-decorators" title="Link to this heading"></a></h1>
<p>Python has a really interesting feature called function decorators. This
allows some really neat things for web applications. Because each view in
Flask is a function, decorators can be used to inject additional
functionality to one or more functions. The <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">route()</span></code></a>
decorator is the one you probably used already. But there are use cases
for implementing your own decorator. For instance, imagine you have a
view that should only be used by people that are logged in. If a user
goes to the site and is not logged in, they should be redirected to the
login page. This is a good example of a use case where a decorator is an
excellent solution.</p>
<section id="login-required-decorator">
<h2>Login Required Decorator<a class="headerlink" href="#login-required-decorator" title="Link to this heading"></a></h2>
<p>So lets implement such a decorator. A decorator is a function that
wraps and replaces another function. Since the original function is
replaced, you need to remember to copy the original functions information
to the new function. Use <a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.wraps" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.wraps()</span></code></a> to handle this for you.</p>
<p>This example assumes that the login page is called <code class="docutils literal notranslate"><span class="pre">'login'</span></code> and that
the current user is stored in <code class="docutils literal notranslate"><span class="pre">g.user</span></code> and is <code class="docutils literal notranslate"><span class="pre">None</span></code> if there is no-one
logged in.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">functools</span><span class="w"> </span><span class="kn">import</span> <span class="n">wraps</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">request</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span>
<span class="k">def</span><span class="w"> </span><span class="nf">login_required</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorated_function</span><span class="p">(</span><span class="o">*</span><span class="n">args</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;login&#39;</span><span class="p">,</span> <span class="nb">next</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">url</span><span class="p">))</span>
<span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</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">decorated_function</span>
</pre></div>
</div>
<p>To use the decorator, apply it as innermost decorator to a view function.
When applying further decorators, always remember
that the <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">route()</span></code></a> decorator is the outermost.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/secret_page&#39;</span><span class="p">)</span>
<span class="nd">@login_required</span>
<span class="k">def</span><span class="w"> </span><span class="nf">secret_page</span><span class="p">():</span>
<span class="k">pass</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The <code class="docutils literal notranslate"><span class="pre">next</span></code> value will exist in <code class="docutils literal notranslate"><span class="pre">request.args</span></code> after a <code class="docutils literal notranslate"><span class="pre">GET</span></code> request for
the login page. Youll have to pass it along when sending the <code class="docutils literal notranslate"><span class="pre">POST</span></code> request
from the login form. You can do this with a hidden input tag, then retrieve it
from <code class="docutils literal notranslate"><span class="pre">request.form</span></code> when logging the user in.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">&lt;</span><span class="nb">input</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&quot;hidden&quot;</span> <span class="n">value</span><span class="o">=</span><span class="s2">&quot;{{ request.args.get(&#39;next&#39;, &#39;&#39;) }}&quot;</span><span class="o">/&gt;</span>
</pre></div>
</div>
</div>
</section>
<section id="caching-decorator">
<h2>Caching Decorator<a class="headerlink" href="#caching-decorator" title="Link to this heading"></a></h2>
<p>Imagine you have a view function that does an expensive calculation and
because of that you would like to cache the generated results for a
certain amount of time. A decorator would be nice for that. Were
assuming you have set up a cache like mentioned in <a class="reference internal" href="caching.html"><span class="doc">Caching</span></a>.</p>
<p>Here is an example cache function. It generates the cache key from a
specific prefix (actually a format string) and the current path of the
request. Notice that we are using a function that first creates the
decorator that then decorates the function. Sounds awful? Unfortunately
it is a little bit more complex, but the code should still be
straightforward to read.</p>
<p>The decorated function will then work as follows</p>
<ol class="arabic simple">
<li><p>get the unique cache key for the current request based on the current
path.</p></li>
<li><p>get the value for that key from the cache. If the cache returned
something we will return that value.</p></li>
<li><p>otherwise the original function is called and the return value is
stored in the cache for the timeout provided (by default 5 minutes).</p></li>
</ol>
<p>Here the code:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">functools</span><span class="w"> </span><span class="kn">import</span> <span class="n">wraps</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">request</span>
<span class="k">def</span><span class="w"> </span><span class="nf">cached</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">60</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s1">&#39;view/</span><span class="si">{}</span><span class="s1">&#39;</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorated_function</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">cache_key</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
<span class="n">rv</span> <span class="o">=</span> <span class="n">cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">cache_key</span><span class="p">)</span>
<span class="k">if</span> <span class="n">rv</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">rv</span>
<span class="n">rv</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="n">cache</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">cache_key</span><span class="p">,</span> <span class="n">rv</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>
<span class="k">return</span> <span class="n">rv</span>
<span class="k">return</span> <span class="n">decorated_function</span>
<span class="k">return</span> <span class="n">decorator</span>
</pre></div>
</div>
<p>Notice that this assumes an instantiated <code class="docutils literal notranslate"><span class="pre">cache</span></code> object is available, see
<a class="reference internal" href="caching.html"><span class="doc">Caching</span></a>.</p>
</section>
<section id="templating-decorator">
<h2>Templating Decorator<a class="headerlink" href="#templating-decorator" title="Link to this heading"></a></h2>
<p>A common pattern invented by the TurboGears guys a while back is a
templating decorator. The idea of that decorator is that you return a
dictionary with the values passed to the template from the view function
and the template is automatically rendered. With that, the following
three examples do exactly the same:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">index</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">&#39;index.html&#39;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">42</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="nd">@templated</span><span class="p">(</span><span class="s1">&#39;index.html&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">index</span><span class="p">():</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">42</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="nd">@templated</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">index</span><span class="p">():</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
</pre></div>
</div>
<p>As you can see, if no template name is provided it will use the endpoint
of the URL map with dots converted to slashes + <code class="docutils literal notranslate"><span class="pre">'.html'</span></code>. Otherwise
the provided template name is used. When the decorated function returns,
the dictionary returned is passed to the template rendering function. If
<code class="docutils literal notranslate"><span class="pre">None</span></code> is returned, an empty dictionary is assumed, if something else than
a dictionary is returned we return it from the function unchanged. That
way you can still use the redirect function or return simple strings.</p>
<p>Here is the code for that decorator:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">functools</span><span class="w"> </span><span class="kn">import</span> <span class="n">wraps</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">request</span><span class="p">,</span> <span class="n">render_template</span>
<span class="k">def</span><span class="w"> </span><span class="nf">templated</span><span class="p">(</span><span class="n">template</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorated_function</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">template_name</span> <span class="o">=</span> <span class="n">template</span>
<span class="k">if</span> <span class="n">template_name</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">template_name</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">request</span><span class="o">.</span><span class="n">endpoint</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;/&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">.html&quot;</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</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">ctx</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
<span class="k">return</span> <span class="n">ctx</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="n">template_name</span><span class="p">,</span> <span class="o">**</span><span class="n">ctx</span><span class="p">)</span>
<span class="k">return</span> <span class="n">decorated_function</span>
<span class="k">return</span> <span class="n">decorator</span>
</pre></div>
</div>
</section>
<section id="endpoint-decorator">
<h2>Endpoint Decorator<a class="headerlink" href="#endpoint-decorator" title="Link to this heading"></a></h2>
<p>When you want to use the werkzeug routing system for more flexibility you
need to map the endpoint as defined in the <a class="reference external" href="https://werkzeug.palletsprojects.com/en/stable/routing/#werkzeug.routing.Rule" title="(in Werkzeug v3.1.x)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Rule</span></code></a>
to a view function. This is possible with this decorator. For example:</p>
<div class="highlight-default 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="kn">from</span><span class="w"> </span><span class="nn">werkzeug.routing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Rule</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">app</span><span class="o">.</span><span class="n">url_map</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">Rule</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="n">endpoint</span><span class="o">=</span><span class="s1">&#39;index&#39;</span><span class="p">))</span>
<span class="nd">@app</span><span class="o">.</span><span class="n">endpoint</span><span class="p">(</span><span class="s1">&#39;index&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">my_index</span><span class="p">():</span>
<span class="k">return</span> <span class="s2">&quot;Hello world&quot;</span>
</pre></div>
</div>
</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="#">View Decorators</a><ul>
<li><a class="reference internal" href="#login-required-decorator">Login Required Decorator</a></li>
<li><a class="reference internal" href="#caching-decorator">Caching Decorator</a></li>
<li><a class="reference internal" href="#templating-decorator">Templating Decorator</a></li>
<li><a class="reference internal" href="#endpoint-decorator">Endpoint Decorator</a></li>
</ul>
</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><a href="../index.html">Overview</a>
<ul>
<li><a href="index.html">Patterns for Flask</a>
<ul>
<li>Previous: <a href="caching.html" title="previous chapter">Caching</a>
<li>Next: <a href="wtforms.html" title="next chapter">Form Validation with WTForms</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>