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

207 lines
16 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>Lazily Loading 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="MongoDB with MongoEngine" href="mongoengine.html" />
<link rel="prev" title="JavaScript, fetch, and JSON" href="javascript.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="mongoengine.html" title="MongoDB with MongoEngine"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="javascript.html" title="JavaScript, fetch, and JSON"
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="">Lazily Loading Views</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="lazily-loading-views">
<h1>Lazily Loading Views<a class="headerlink" href="#lazily-loading-views" title="Link to this heading"></a></h1>
<p>Flask is usually used with the decorators. Decorators are simple and you
have the URL right next to the function that is called for that specific
URL. However there is a downside to this approach: it means all your code
that uses decorators has to be imported upfront or Flask will never
actually find your function.</p>
<p>This can be a problem if your application has to import quick. It might
have to do that on systems like Googles App Engine or other systems. So
if you suddenly notice that your application outgrows this approach you
can fall back to a centralized URL mapping.</p>
<p>The system that enables having a central URL map is the
<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> function. Instead of using decorators,
you have a file that sets up the application with all URLs.</p>
<section id="converting-to-centralized-url-map">
<h2>Converting to Centralized URL Map<a class="headerlink" href="#converting-to-centralized-url-map" title="Link to this heading"></a></h2>
<p>Imagine the current application looks somewhat like this:</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="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">index</span><span class="p">():</span>
<span class="k">pass</span>
<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/user/&lt;username&gt;&#39;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">user</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
<span class="k">pass</span>
</pre></div>
</div>
<p>Then, with the centralized approach you would have one file with the views
(<code class="file docutils literal notranslate"><span class="pre">views.py</span></code>) but without any decorator:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">index</span><span class="p">():</span>
<span class="k">pass</span>
<span class="k">def</span><span class="w"> </span><span class="nf">user</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
<span class="k">pass</span>
</pre></div>
</div>
<p>And then a file that sets up an application which maps the functions to
URLs:</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">yourapplication</span><span class="w"> </span><span class="kn">import</span> <span class="n">views</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">add_url_rule</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">views</span><span class="o">.</span><span class="n">index</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;/user/&lt;username&gt;&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">views</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="loading-late">
<h2>Loading Late<a class="headerlink" href="#loading-late" title="Link to this heading"></a></h2>
<p>So far we only split up the views and the routing, but the module is still
loaded upfront. The trick is to actually load the view function as needed.
This can be accomplished with a helper class that behaves just like a
function but internally imports the real function on first use:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">werkzeug.utils</span><span class="w"> </span><span class="kn">import</span> <span class="n">import_string</span><span class="p">,</span> <span class="n">cached_property</span>
<span class="k">class</span><span class="w"> </span><span class="nc">LazyView</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">import_name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__module__</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__name__</span> <span class="o">=</span> <span class="n">import_name</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">import_name</span> <span class="o">=</span> <span class="n">import_name</span>
<span class="nd">@cached_property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">view</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">import_string</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">import_name</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</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="bp">self</span><span class="o">.</span><span class="n">view</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>
</pre></div>
</div>
<p>Whats important here is is that <code class="code docutils literal notranslate"><span class="pre">__module__</span></code> and <code class="code docutils literal notranslate"><span class="pre">__name__</span></code> are properly
set. This is used by Flask internally to figure out how to name the
URL rules in case you dont provide a name for the rule yourself.</p>
<p>Then you can define your central place to combine the views like this:</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">yourapplication.helpers</span><span class="w"> </span><span class="kn">import</span> <span class="n">LazyView</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">add_url_rule</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span>
<span class="n">view_func</span><span class="o">=</span><span class="n">LazyView</span><span class="p">(</span><span class="s1">&#39;yourapplication.views.index&#39;</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;/user/&lt;username&gt;&#39;</span><span class="p">,</span>
<span class="n">view_func</span><span class="o">=</span><span class="n">LazyView</span><span class="p">(</span><span class="s1">&#39;yourapplication.views.user&#39;</span><span class="p">))</span>
</pre></div>
</div>
<p>You can further optimize this in terms of amount of keystrokes needed to
write this by having a function that calls into
<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> by prefixing a string with the project
name and a dot, and by wrapping <code class="code docutils literal notranslate"><span class="pre">view_func</span></code> in a <code class="code docutils literal notranslate"><span class="pre">LazyView</span></code> as needed.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">url</span><span class="p">(</span><span class="n">import_name</span><span class="p">,</span> <span class="n">url_rules</span><span class="o">=</span><span class="p">[],</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">LazyView</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;yourapplication.</span><span class="si">{</span><span class="n">import_name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">for</span> <span class="n">url_rule</span> <span class="ow">in</span> <span class="n">url_rules</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="n">url_rule</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">view</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span>
<span class="c1"># add a single route to the index view</span>
<span class="n">url</span><span class="p">(</span><span class="s1">&#39;views.index&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;/&#39;</span><span class="p">])</span>
<span class="c1"># add two routes to a single function endpoint</span>
<span class="n">url_rules</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;/user/&#39;</span><span class="p">,</span><span class="s1">&#39;/user/&lt;username&gt;&#39;</span><span class="p">]</span>
<span class="n">url</span><span class="p">(</span><span class="s1">&#39;views.user&#39;</span><span class="p">,</span> <span class="n">url_rules</span><span class="p">)</span>
</pre></div>
</div>
<p>One thing to keep in mind is that before and after request handlers have
to be in a file that is imported upfront to work properly on the first
request. The same goes for any kind of remaining decorator.</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="#">Lazily Loading Views</a><ul>
<li><a class="reference internal" href="#converting-to-centralized-url-map">Converting to Centralized URL Map</a></li>
<li><a class="reference internal" href="#loading-late">Loading Late</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="javascript.html" title="previous chapter">JavaScript, <code class="docutils literal notranslate"><span class="pre">fetch</span></code>, and JSON</a>
<li>Next: <a href="mongoengine.html" title="next chapter">MongoDB with MongoEngine</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>