269 lines
22 KiB
HTML
269 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="en" data-content_root="../">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>View Decorators — 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> »</li>
|
||
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Patterns for Flask</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">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 let’s 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 function’s 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">'login'</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">'/secret_page'</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. You’ll 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"><</span><span class="nb">input</span> <span class="nb">type</span><span class="o">=</span><span class="s2">"hidden"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"{{ request.args.get('next', '') }}"</span><span class="o">/></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. We’re
|
||
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">'view/</span><span class="si">{}</span><span class="s1">'</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">'/'</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">'index.html'</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">'/'</span><span class="p">)</span>
|
||
<span class="nd">@templated</span><span class="p">(</span><span class="s1">'index.html'</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">'/'</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">"</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">'.'</span><span class="p">,</span><span class="w"> </span><span class="s1">'/'</span><span class="p">)</span><span class="si">}</span><span class="s2">.html"</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">'/'</span><span class="p">,</span> <span class="n">endpoint</span><span class="o">=</span><span class="s1">'index'</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">'index'</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">"Hello world"</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">
|
||
© Copyright 2010 Pallets.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
|
||
</div>
|
||
</body>
|
||
</html>
|