<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flask on Dapeng Zhang</title><link>https://zhangdapeng.dev/tags/flask/</link><description>Recent content in Flask on Dapeng Zhang</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Wed, 30 Jul 2025 10:40:51 +0800</lastBuildDate><atom:link href="https://zhangdapeng.dev/tags/flask/index.xml" rel="self" type="application/rss+xml"/><item><title>flask使用sqlalchemy实现一个group by并且order by的查询</title><link>https://zhangdapeng.dev/p/flask-shi-yong-sqlalchemy-shi-xian-yi-ge-group-by-bing-qie-order-by-de-cha-xun/</link><pubDate>Wed, 30 Jul 2025 10:40:51 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-shi-yong-sqlalchemy-shi-xian-yi-ge-group-by-bing-qie-order-by-de-cha-xun/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1565854753/walkerfree/python.jpg" alt="Featured image of post flask使用sqlalchemy实现一个group by并且order by的查询" /&gt;&lt;p&gt;比如我要将我的Tags表的title，按照使用次数最多的并进行降序排列，而且获取前20条Tag&lt;br&gt;
如果是sql查询的话，如果下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;select&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;count&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; total, title 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; tags 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;where&lt;/span&gt; title &lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;group&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;by&lt;/span&gt; title 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;order&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;by&lt;/span&gt; total &lt;span style="color:#66d9ef"&gt;DESC&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;limit&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用sqlalchemy的使用方式如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; sqlalchemy &lt;span style="color:#f92672"&gt;import&lt;/span&gt; func
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;with_entities(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title, func&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;label(&lt;span style="color:#e6db74"&gt;&amp;#39;total&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;group_by(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;order_by(&lt;span style="color:#e6db74"&gt;&amp;#39;total DESC&amp;#39;&lt;/span&gt;)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;limit(&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是上面的使用方式在执行的时候会报如下错误&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SAWarning: Can&lt;span style="color:#e6db74"&gt;&amp;#39;t resolve label reference &amp;#39;&lt;/span&gt;total DESC&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;; converting to text&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;this warning may be suppressed after &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; occurrences&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; util.ellipses_string&lt;span style="color:#f92672"&gt;(&lt;/span&gt;element.element&lt;span style="color:#f92672"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;解决方案的代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; sqlalchemy &lt;span style="color:#f92672"&gt;import&lt;/span&gt; func, desc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;order_by_total &lt;span style="color:#f92672"&gt;=&lt;/span&gt; func&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;label(&lt;span style="color:#e6db74"&gt;&amp;#39;total&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;with_entities(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title, func&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;label(&lt;span style="color:#e6db74"&gt;&amp;#39;total&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;group_by(Tag&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;order_by(desc(order_by_total))\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;limit(&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;)\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用方式还是挺奇怪的&lt;/p&gt;</description></item><item><title>如何在Flask扩展开发的过程中加入异常日志</title><link>https://zhangdapeng.dev/p/ru-he-zai-flask-kuo-zhan-kai-fa-de-guo-cheng-zhong-jia-ru-yi-chang-ri-zhi/</link><pubDate>Wed, 30 Jul 2025 09:57:08 +0800</pubDate><guid>https://zhangdapeng.dev/p/ru-he-zai-flask-kuo-zhan-kai-fa-de-guo-cheng-zhong-jia-ru-yi-chang-ri-zhi/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post 如何在Flask扩展开发的过程中加入异常日志" /&gt;&lt;p&gt;之前开发了一个扩展叫做&lt;a class="link" href="https://github.com/durban89/flask_dxcaptcha" target="_blank" rel="noopener"
 &gt;flask_dxcaptcha&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最近在使用他的过程中发现了一个问题，发现异常的时候直接导致使用的他的程序奔溃了，异常让人崩溃，但是不能给使用者带来崩溃，于是做了修复，加了下日志处理逻辑。&lt;/p&gt;
&lt;p&gt;但是如果自己再定义一个好像显得有些麻烦，毕竟定义一套虽说可以更加责任化（就是出了问题知道是自己扩展的问题），但是既然是Flask扩展，应该跟使用Flask框架结合下会更好，于是决定先暂时这样处理吧，添加的代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; current_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;......&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exception(str(e))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;......&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;测试如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; current_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;测试异常&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;......&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exception(str(e))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;......&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;于是得到的异常结果如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-12-27 10:51:12,653&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ERROR in captchaclient: 测试异常
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Traceback &lt;span style="color:#f92672"&gt;(&lt;/span&gt;most recent call last&lt;span style="color:#f92672"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; File &lt;span style="color:#e6db74"&gt;&amp;#34;/Users/durban/python/baby/.env3_baby/lib/python3.7/site-packages/flask_dxcaptcha/captchaclient.py&amp;#34;&lt;/span&gt;, line 57, in checkToken
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; raise Exception&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;测试异常&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Exception: 测试异常
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>如何发布一个Flask扩展包</title><link>https://zhangdapeng.dev/p/ru-he-fa-bu-yi-ge-flask-kuo-zhan-bao/</link><pubDate>Wed, 30 Jul 2025 09:56:59 +0800</pubDate><guid>https://zhangdapeng.dev/p/ru-he-fa-bu-yi-ge-flask-kuo-zhan-bao/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post 如何发布一个Flask扩展包" /&gt;&lt;p&gt;如何开发和发布一个Flask扩展，如何开发一个Flask扩展，官方介绍在&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/extensiondev/" target="_blank" rel="noopener"
 &gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="如何开发flask扩展---实战篇以我最近开发的flask_dxcaptcha为例"&gt;如何开发Flask扩展 - 实战篇（以我最近开发的flask_dxcaptcha为例）
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;OS - MAX OS&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;创建一个目录flask_dxcaptcha&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir flask_dxcaptcha
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd flask_dxcaptcha
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;创建python虚拟环境并激活&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;virtualenv .env -p /usr/local/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source .env/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;创建包并开发具体的逻辑(flask_dxcaptcha目录下)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir flask_dxcaptcha
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd flask_dxcaptcha
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;touch __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;编写__init__.py文件开始开发扩展逻辑，记录下flask_dxcaptcha的__init__.py&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Author: durban.zhang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Date: 2019-12-25 17:02:16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified by: durban&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified time: 2019-12-25 18:43:25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; current_app, _app_ctx_stack
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask_dxcaptcha.captchaclient &lt;span style="color:#f92672"&gt;import&lt;/span&gt; CaptchaClient
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DXCaptcha&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; app &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;init_app&lt;/span&gt;(self, app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setdefault(&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_ID&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setdefault(&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_SECRECT&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;init_captcha_client&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_ID&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;or&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_SECRECT&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_ID and DX_APP_SECRECT can not empty.&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; CaptchaClient(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_ID&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;DX_APP_SECRECT&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ctx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _app_ctx_stack&lt;span style="color:#f92672"&gt;.&lt;/span&gt;top
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ctx &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; hasattr(ctx, &lt;span style="color:#e6db74"&gt;&amp;#39;dx_captcha&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ctx&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dx_captcha &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_captcha_client()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ctx&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dx_captcha
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;flask_dxcaptcha包下的目录结构如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── captchaclient.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── captcharesponse.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── ctuclient.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── cturequest.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── cturesponse.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── cturesponsestatus.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── cturesult.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── hitrule.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── risklevel.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── risktype.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── suggestion.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── suggestpolicy.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后先项目目录下面添加如下几个文件 setup.py # 具体打包和安装配置 MANIFEST.in # 打包文件配置 README.md # 项目介绍包括如何安装、如何使用 LICENSE # 项目许可类型&lt;/p&gt;
&lt;p&gt;这里比较重要的是setup.py和MANIFEST.in，着重记录下这两个文件，其余两个文件可以去参考这个扩展&lt;a class="link" href="https://github.com/durban89/flask_dxcaptcha" target="_blank" rel="noopener"
 &gt;flask_dxcaptcha&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;setup.py的配置如下&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Author: durban&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Date: 2019-12-25 18:00:43&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified by: durban.zhang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified time: 2019-12-25 19:03:55&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; setuptools &lt;span style="color:#f92672"&gt;import&lt;/span&gt; find_packages, setup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;setup(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Flask-DXCaptcha&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;1.0.0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;https://github.com/durban89/flask_dxcaptcha&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; license&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;MIT&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;durban zhang&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author_email&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;[email protected]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Flask-DXCaptcha是依赖顶象科技提供的无感验证功能开发的Flask 扩展&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; long_description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;__doc__,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; packages&lt;span style="color:#f92672"&gt;=&lt;/span&gt;find_packages(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zip_safe&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; include_package_data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; platforms&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;any&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; install_requires&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Flask&amp;gt;=1.1.1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; python_requires&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;gt;=3.7.4&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; classifiers&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Environment :: Web Environment&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Intended Audience :: Developers&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;License :: OSI Approved :: BSD License&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Operating System :: OS Independent&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Programming Language :: Python :: 3&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Topic :: Internet :: WWW/HTTP :: Dynamic Content&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Topic :: Software Development :: Libraries :: Python Modules&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;具体关于setup参数的介绍，请点&lt;a class="link" href="https://packaging.python.org/guides/distributing-packages-using-setuptools/" target="_blank" rel="noopener"
 &gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MANIFEST.in的配置如下&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;include MANIFEST.in
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;include README
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;include LICENSE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;recursive-exclude *.pyc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;具体关于参数的介绍，请点&lt;a class="link" href="https://packaging.python.org/guides/using-manifest-in/" target="_blank" rel="noopener"
 &gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="如何发布flask扩展"&gt;如何发布Flask扩展
&lt;/h3&gt;&lt;p&gt;如何发布Flask扩展，具体的可以参考官方介绍，点&lt;a class="link" href="https://packaging.python.org/" target="_blank" rel="noopener"
 &gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这里以flask_dxcaptcha为例介绍下，&lt;strong&gt;前提是项目开发后，自己测试没有问题再发布&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在flask_dxcaptcha项目目录下面运行如下命令（&lt;strong&gt;注意是我们的虚拟环境&lt;/strong&gt;）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果没有安装&lt;code&gt;setuptools&lt;/code&gt;和&lt;code&gt;wheel&lt;/code&gt;执行下面命令&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install setuptools wheel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;打包扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python3 setup.py sdist bdist_wheel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在dist目录下生成如下文件&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ tree dist
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dist
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── Flask-DXCaptcha-1.0.0.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── Flask_DXCaptcha-1.0.0-py3-none-any.whl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;上传包到&lt;code&gt;Test PyPI&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注册&lt;code&gt;Test PyPI&lt;/code&gt;账号，点&lt;a class="link" href="https://test.pypi.org/account/register/" target="_blank" rel="noopener"
 &gt;这里&lt;/a&gt;注册，然后安装twine，执行下面命令&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install twine -i https://mirrors.aliyun.com/pypi/simple/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;-i 参数指定了包的镜像源&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;上传所有打包文件到&lt;code&gt;Test PyPI&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;twine upload --repository-url https://test.pypi.org/legacy/ dist/*
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;示例如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ twine upload --repository-url https://test.pypi.org/legacy/ ./dist/*
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Uploading distributions to https://test.pypi.org/legacy/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Enter your username: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;your_username&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Enter your password:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Uploading Flask_DXCaptcha-1.0.0-py3-none-any.whl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;100%|███████████████████████████████████████████████████████████████████████████████████████████████| 13.0k/13.0k &lt;span style="color:#f92672"&gt;[&lt;/span&gt;00:04&amp;lt;00:00, 3.01kB/s&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Uploading Flask-DXCaptcha-1.0.0.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;100%|███████████████████████████████████████████████████████████████████████████████████████████████| 9.53k/9.53k &lt;span style="color:#f92672"&gt;[&lt;/span&gt;00:01&amp;lt;00:00, 7.57kB/s&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;View at:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://test.pypi.org/project/Flask-DXCaptcha/1.0.0/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;测试安装上传的包&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install -i https://test.pypi.org/simple/ --no-deps Flask-DXCaptcha&lt;span style="color:#f92672"&gt;==&lt;/span&gt;1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果安装成功，就可以正常使用了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Python 3.7.4 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;default, Jul &lt;span style="color:#ae81ff"&gt;9&lt;/span&gt; 2019, 18:14:44&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;Clang 9.0.0 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;clang-900.0.39.2&lt;span style="color:#f92672"&gt;)]&lt;/span&gt; on darwin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Type &lt;span style="color:#e6db74"&gt;&amp;#34;help&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;copyright&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;credits&amp;#34;&lt;/span&gt; or &lt;span style="color:#e6db74"&gt;&amp;#34;license&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; more information.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; import flask_dxcaptcha
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果以上都是正常包的话，那么将包发布到正式的PyPI中&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;twine upload dist/*
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;示例如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ twine upload dist/*
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Uploading distributions to https://upload.pypi.org/legacy/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Enter your username: durban_zhang
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Enter your password:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Uploading Flask_DXCaptcha-1.0.0-py3-none-any.whl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;100%|███████████████████████████████████████████████████████████████████████████████████████████████| 13.0k/13.0k &lt;span style="color:#f92672"&gt;[&lt;/span&gt;00:09&amp;lt;00:00, 1.41kB/s&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Uploading Flask-DXCaptcha-1.0.0.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;100%|███████████████████████████████████████████████████████████████████████████████████████████████| 9.53k/9.53k &lt;span style="color:#f92672"&gt;[&lt;/span&gt;00:01&amp;lt;00:00, 6.87kB/s&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;View at:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://pypi.org/project/Flask-DXCaptcha/1.0.0/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;打开地址 &lt;a class="link" href="https://pypi.org/project/Flask-DXCaptcha/1.0.0/" target="_blank" rel="noopener"
 &gt;https://pypi.org/project/Flask-DXCaptcha/1.0.0/&lt;/a&gt; 就能看到发布的Flask扩展了&lt;/p&gt;</description></item><item><title>Flask 1.0进阶 - 扩展</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-kuo-zhan/</link><pubDate>Wed, 30 Jul 2025 09:56:50 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-kuo-zhan/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0进阶 - 扩展" /&gt;&lt;p&gt;扩展是为flask应用程序添加功能的额外包。例如，扩展可能会添加对发送电子邮件或连接到数据库的支持。一些扩展添加了全新的框架来帮助构建某些类型的应用程序，比如REST API。&lt;/p&gt;
&lt;h3 id="查找扩展"&gt;查找扩展
&lt;/h3&gt;&lt;p&gt;Flask扩展通常命名为“Flask-Foo”或“Foo-Flask”。您可以在PyPI中搜索带有 &lt;a class="link" href="https://pypi.org/search/?c=Framework&amp;#43;%3A%3A&amp;#43;Flask" target="_blank" rel="noopener"
 &gt;Framework :: Flask&lt;/a&gt; 标记的包。&lt;/p&gt;
&lt;h3 id="使用扩展"&gt;使用扩展
&lt;/h3&gt;&lt;p&gt;关于每个扩展的安装、配置和使用说明，请查询他们的文档。通常，扩展从 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.config" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.config&lt;/strong&gt;&lt;/a&gt; 获取他们自己的配置参数并在初始化的时候传参到应用程序实例中。举个例子，比如扩展“Flask-Foo”的使用方式可能如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask_foo &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Foo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;foo &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Foo()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FOO_BAR&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FOO_SPAM&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;eggs&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;foo&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="创建扩展"&gt;创建扩展
&lt;/h3&gt;&lt;p&gt;尽管PyPI包含了许多Flask扩展，但是仍没有找到适合你的扩展。如果是这样的话，你可以创建一个你自己的扩展。请参考&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/extensiondev/#extension-dev" target="_blank" rel="noopener"
 &gt;Flask Extension Development&lt;/a&gt;来开发你自己的Flask扩展。&lt;/p&gt;
&lt;h2 id="命令行界面"&gt;命令行界面
&lt;/h2&gt;&lt;p&gt;安装Flask后，会默认安装&lt;code&gt;flask&lt;/code&gt;脚本的，这个脚本一个&lt;a class="link" href="https://click.palletsprojects.com/" target="_blank" rel="noopener"
 &gt;Click&lt;/a&gt;命令行接口会被安装到虚拟环境中。&lt;/p&gt;
&lt;p&gt;通过终端，这个脚本可以访问内建的命令、扩展的命令和程序定义的命令。使用&lt;code&gt;--help&lt;/code&gt;选项可以获取到更多关于命令的信息和关于命令的选项&lt;/p&gt;
&lt;h3 id="应用程序发现"&gt;应用程序发现
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;flask&lt;/code&gt;命令是被Flask安装的，不是你的应用安装的。为了使用这个命令，必须告诉你的应用程序在哪里。&lt;code&gt;FLASK_APP&lt;/code&gt;环境变量则是用来告诉在哪里能找到应用程序&lt;/p&gt;
&lt;p&gt;使用方式如下：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Unix Bash (Linux, Mac, etc.):&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hello
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Windows CMD:&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; set FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hello
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Windows PowerShell:&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; $env:FLASK_APP &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;FLASK_APP&lt;/code&gt;支持多种形式的选项来具体化你的应用程序，大多数都是用比较简单的方式。&lt;/p&gt;
&lt;p&gt;下面是一些比较典型的例子&lt;/p&gt;
&lt;p&gt;(不做任何设置)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wsgi.py文件将被导入，并自动的检查是否有app&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;app&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;，它提供了最简单的方式去创建了一个带有额外参数的工厂函数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;FLASK_APP=hello&lt;/code&gt; 名字将被导入，并自动检查是否有app(&lt;code&gt;app&lt;/code&gt;)或者工厂函数(&lt;code&gt;create_app&lt;/code&gt;)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;code&gt;FLASK_APP&lt;/code&gt;有三个部分：一个设置了当前工作目录的可选路径，一个Python文件或者有点的导入路径和一个可选的实例或者工厂函数变量名。如果是一个工厂函数的话可以通过小括号传入需要的参数，下面看下具体的例子&lt;/p&gt;
&lt;p&gt;&lt;code&gt;FLASK_APP=src/hello&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;设置当前目录的src目录并导入hello
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;FLASK_APP=hello.web&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;导入&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;hello.web&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;路径
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;FLASK_APP=hello:app2&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;使用在hello中的&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;app2&lt;span style="color:#e6db74"&gt;```&lt;/span&gt; Flask实例
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;FLASK_APP=&amp;quot;hello:create_app('dev')&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;create_app&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;工厂函数在&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;hello&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;中被调用并传递参数&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;dev&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;字符串
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果&lt;code&gt;FLASK_APP&lt;/code&gt;没有设置，执行命令时将尝试导入“app”或者“wsgi”（作为一个“.py”文件，或者包）并尝试去检查一个应用实例或者工厂&lt;/p&gt;
&lt;p&gt;如果导入的信息被设置了，即&lt;code&gt;FLASK_APP&lt;/code&gt;被设置了的话。执行命令时，程序查找一个应用实例名字叫做&lt;code&gt;app&lt;/code&gt;或者&lt;code&gt;application&lt;/code&gt;，它是一个应用的实例。如果没有找到实例，程序会查找一个工厂行数，名称是&lt;code&gt;create_app&lt;/code&gt;或者&lt;code&gt;make_app&lt;/code&gt;，它会返回一个实例&lt;/p&gt;
&lt;p&gt;当应用工厂函数被调用，如果工厂函数带有一个名字叫做&lt;code&gt;script_info&lt;/code&gt;的参数，则&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.cli.ScriptInfo" target="_blank" rel="noopener"
 &gt;ScriptInfo&lt;/a&gt;实例会作为一个关键字参数传递进去。如果工厂函数只带有一个参数并且没有圆括号跟在工厂函数名字后面，则ScriptInfo实例将作为一个位置参数传递进去。如果圆括号跟在工厂函数后面的话，他们的内容将被解析喂Python字符串并作为参数传入到函数中。这意味着字符串始终在引号中。&lt;/p&gt;
&lt;h3 id="启动开发服务器"&gt;启动开发服务器
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;run&lt;/code&gt;命令将启动一个开发服务器，在许多情况下替代了 &lt;strong&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.run" target="_blank" rel="noopener"
 &gt;Flask.run()&lt;/a&gt;&lt;/strong&gt; 方法&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flask run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Serving Flask app &lt;span style="color:#e6db74"&gt;&amp;#34;baby&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;lazy loading&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Environment: development
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debug mode: on
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Running on http://127.0.0.1:5000/ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Restarting with stat
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debugger is active!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debugger PIN: 267-735-523
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;注意&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;不要使用&lt;code&gt;run&lt;/code&gt;命令再生产环境中，只使用开发服务器在开发环境中。开发服务器虽然提供了便利，但是不是安全的、不是稳定的、不是高效率的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进阶结束更新说明&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先说下此系列的进阶，主要是以官方文档为主线，边阅读边实践，并记录分享，其中有部分官方的实例是有问题的，但是没有找到反馈入口，也出于没有人访问的情况，所以结束继续分享。如果有疑问可以转到官方文档&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/" target="_blank" rel="noopener"
 &gt;这里&lt;/a&gt;去查看&lt;/p&gt;</description></item><item><title>flask在pytest中如何使用session</title><link>https://zhangdapeng.dev/p/flask-zai-pytest-zhong-ru-he-shi-yong-session/</link><pubDate>Wed, 30 Jul 2025 09:56:47 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-zai-pytest-zhong-ru-he-shi-yong-session/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post flask在pytest中如何使用session" /&gt;&lt;p&gt;先看代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AuthActions&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;(self, username&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, password&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, code&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;1111&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;session_transaction() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; session:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session[&lt;span style="color:#e6db74"&gt;&amp;#39;captcha_key&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1111&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/auth/login&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;: username, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;: password,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;verification-code&amp;#39;&lt;/span&gt;: code}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;logout&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/auth/logout&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里为什么会提到session，因为我们正常使用的过程中，在登录或者注册的逻辑中会遇到session记录验证码，然后用户用户进行登录或者注册提交表单时的验证，但是这个逻辑在pytest中会遇到一些问题，因为要伪造一个类似于session的情况来测试类似登录或者注册的逻辑中使用session的情况。&lt;/p&gt;
&lt;p&gt;这个问题在flask的官网也有介绍如何在测试环境中使用session，可以参考&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/testing/#accessing-and-modifying-sessions" target="_blank" rel="noopener"
 &gt;https://flask.palletsprojects.com/en/1.1.x/testing/#accessing-and-modifying-sessions&lt;/a&gt;&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.testing.FlaskClient.session" target="_blank" rel="noopener"
 &gt;https://flask.palletsprojects.com/en/1.1.x/api/#flask.testing.FlaskClient.session&lt;/a&gt;_transaction&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;都有对应的说明但是具体没有实例的话时没有办法具体了解其使用场景的&lt;/p&gt;
&lt;p&gt;不过今天在进行测试单元改写的时候发现原来可以这样使用 特此记录下方便日查找&lt;/p&gt;
&lt;p&gt;上段代码是自我在conftest.py中的代码&lt;/p&gt;
&lt;p&gt;使用的时候可以像下面这样调用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth&lt;/span&gt;(client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; AuthActions(client)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在测试函数中可以直接这样写&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_login&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# ... 省略 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# ... 省略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然还要自己定义一个叫做client的fixture&lt;/p&gt;</description></item><item><title>Flask中如何获取Server的相关信息</title><link>https://zhangdapeng.dev/p/flask-zhong-ru-he-huo-qu-server-de-xiang-guan-xin-xi/</link><pubDate>Wed, 30 Jul 2025 09:55:29 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-zhong-ru-he-huo-qu-server-de-xiang-guan-xin-xi/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask中如何获取Server的相关信息" /&gt;&lt;p&gt;在使用Flask做web开发的时候，偶尔会需要调用Request中的Server信息，比如端口号，域名之类的&lt;/p&gt;
&lt;p&gt;在Flask中这个Server的相关信息获取是非常简单的&lt;/p&gt;
&lt;p&gt;通过&lt;code&gt;request.environ&lt;/code&gt;可以获取到几乎全部的Server信息&lt;/p&gt;
&lt;p&gt;比如我这里访问&lt;code&gt;http://127.0.0.1:5000/post/item&lt;/code&gt; 然后将&lt;code&gt;request.environ&lt;/code&gt;的信息输出到日志，可以获取到类似如下的信息&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.errors&amp;#39;&lt;/span&gt;: &amp;lt;_io.TextIOWrapper name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;stderr&amp;gt;&amp;#39;&lt;/span&gt; mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;w&amp;#39;&lt;/span&gt; encoding&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;UTF-8&amp;#39;&lt;/span&gt;&amp;gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.version&amp;#39;&lt;/span&gt;: &lt;span style="color:#f92672"&gt;(&lt;/span&gt;1, 0&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.multithread&amp;#39;&lt;/span&gt;: True, &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.multiprocess&amp;#39;&lt;/span&gt;: False, &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.run_once&amp;#39;&lt;/span&gt;: False, &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.url_scheme&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;http&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;REQUEST_METHOD&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;SCRIPT_NAME&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;RAW_PATH_INFO&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;/post/item&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;PATH_INFO&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;/post/item&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;CONTENT_TYPE&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;SERVER_PROTOCOL&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP/1.0&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;SERVER_NAME&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;SERVER_PORT&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;5000&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;REMOTE_ADDR&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;REMOTE_PORT&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;58405&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;GATEWAY_INTERFACE&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;CGI/1.1&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;headers_raw&amp;#39;&lt;/span&gt;: &lt;span style="color:#f92672"&gt;((&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Host&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1:5000&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Connection&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;keep-alive&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Cache-Control&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;max-age=0&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Upgrade-Insecure-Requests&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;User-Agent&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Sec-Fetch-User&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;?1&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Accept&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Sec-Fetch-Site&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Sec-Fetch-Mode&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;navigate&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Accept-Encoding&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;gzip, deflate, br&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Accept-Language&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,lb;q=0.5,co;q=0.4&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Cookie&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;access_token=4b68629fe81a7982728adbea50357c1d382017b8; is_hide_download=true; isShowRedpacket=1; platform=web; QN_HIDDEN_INVEST_TIPS=; QN_HIDDEN_PRODUCT_BUY_PAGE_TIPS=; QN_saveplan_product_id=193; web_device_key=BWDFI2CF7ATW30TXPH07PK2BQTU0YE; zg_did=%7B%22did%22%3A%20%2216680adccf7a2c-0f4a3f6c3160ce-2d604637-3d10d-16680adccf8845%22%7D; _ga=GA1.1.430310970.1544409236; __wzd96bb85a21755618844b8=1556007296|d5311aa69976; UM_distinctid=16b4e888bbd24a-0fffcd4765422c-37677e05-fa000-16b4e888bbeb3; zg_99e520e4c4f745888ab0223f331b0032=%7B%22sid%22%3A%201567655012.547%2C%22updated%22%3A%201567655012.547%2C%22info%22%3A%201567395368068%7D; responseTimeline=1939; io=gwqkVXtguegCXzK5AAAD; __wzdd49646447596a96dfec7=1570773843|a90819629f09; __wzdeea8348a655ff8b452a8=1571706979|dee6f4a108b0; CNZZDATA5587427=cnzz_eid%3D52917038-1544409237-http%253A%252F%252F127.0.0.1%253A3041%252F%26ntime%3D1573781899; Hm_lvt_1ac5a848f7ad883bf5520aa84eda1200=1573781902; Hm_lpvt_1ac5a848f7ad883bf5520aa84eda1200=1573784001; __wzd3bcf756fc5845d894763=1574414880|4bfd4bec505e&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;))&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_HOST&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1:5000&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_CONNECTION&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;keep-alive&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_CACHE_CONTROL&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;max-age=0&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_UPGRADE_INSECURE_REQUESTS&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_USER_AGENT&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_SEC_FETCH_USER&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;?1&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_ACCEPT&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_SEC_FETCH_SITE&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_SEC_FETCH_MODE&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;navigate&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_ACCEPT_ENCODING&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;gzip, deflate, br&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_ACCEPT_LANGUAGE&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,lb;q=0.5,co;q=0.4&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_COOKIE&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;access_token=4b68629fe81a7982728adbea50357c1d382017b8; is_hide_download=true; isShowRedpacket=1; platform=web; QN_HIDDEN_INVEST_TIPS=; QN_HIDDEN_PRODUCT_BUY_PAGE_TIPS=; QN_saveplan_product_id=193; web_device_key=BWDFI2CF7ATW30TXPH07PK2BQTU0YE; zg_did=%7B%22did%22%3A%20%2216680adccf7a2c-0f4a3f6c3160ce-2d604637-3d10d-16680adccf8845%22%7D; _ga=GA1.1.430310970.1544409236; __wzd96bb85a21755618844b8=1556007296|d5311aa69976; UM_distinctid=16b4e888bbd24a-0fffcd4765422c-37677e05-fa000-16b4e888bbeb3; zg_99e520e4c4f745888ab0223f331b0032=%7B%22sid%22%3A%201567655012.547%2C%22updated%22%3A%201567655012.547%2C%22info%22%3A%201567395368068%7D; responseTimeline=1939; io=gwqkVXtguegCXzK5AAAD; __wzdd49646447596a96dfec7=1570773843|a90819629f09; __wzdeea8348a655ff8b452a8=1571706979|dee6f4a108b0; CNZZDATA5587427=cnzz_eid%3D52917038-1544409237-http%253A%252F%252F127.0.0.1%253A3041%252F%26ntime%3D1573781899; Hm_lvt_1ac5a848f7ad883bf5520aa84eda1200=1573781902; Hm_lpvt_1ac5a848f7ad883bf5520aa84eda1200=1573784001; __wzd3bcf756fc5845d894763=1574414880|4bfd4bec505e&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi.input&amp;#39;&lt;/span&gt;: &amp;lt;eventlet.wsgi.Input object at 0x109933550&amp;gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;eventlet.input&amp;#39;&lt;/span&gt;: &amp;lt;eventlet.wsgi.Input object at 0x109933550&amp;gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;eventlet.posthooks&amp;#39;&lt;/span&gt;: &lt;span style="color:#f92672"&gt;[]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;flask.app&amp;#39;&lt;/span&gt;: &amp;lt;Flask &lt;span style="color:#e6db74"&gt;&amp;#39;baby&amp;#39;&lt;/span&gt;&amp;gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;werkzeug.request&amp;#39;&lt;/span&gt;: &amp;lt;Request &lt;span style="color:#e6db74"&gt;&amp;#39;http://127.0.0.1:5000/post/item&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;GET&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&amp;gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里我们看下常见的Server有哪些&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;HTTP_HOST&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;HTTP_USER_AGENT&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;HTTP_ACCEPT&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;HTTP_COOKIE&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;SERVER_NAME&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;SERVER_PORT&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;REMOTE_ADDR&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;REMOTE_PORT&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;REQUEST_METHOD&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;SCRIPT_NAME&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;RAW_PATH_INFO&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;PATH_INFO&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;CONTENT_TYPE&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;SERVER_PROTOCOL&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;当我们想要获取其中的值的话，可以使用下面的方法 比如获取SERVER_NAME的值&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;environ&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;SERVER_NAME&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 - Celery管理后台任务-工厂模式</title><link>https://zhangdapeng.dev/p/flask-1-0-celery-guan-li-hou-tai-ren-wu-gong-chang-mo-shi/</link><pubDate>Tue, 29 Jul 2025 10:56:33 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-celery-guan-li-hou-tai-ren-wu-gong-chang-mo-shi/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 - Celery管理后台任务-工厂模式" /&gt;&lt;p&gt;管理后台任务，在许多项目中都是必不可少的，Flask的文档中推荐的Celery，但是官方文档简单介绍了非工厂模式的使用方式，简单的介绍了使用方法，但是对于使用工厂模式创建应用的我来说，完全达不到我使用的目的，根本不完美&lt;/p&gt;
&lt;p&gt;文章参考&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;a class="link" href="https://citizen-stig.github.io/2016/02/17/using-celery-with-flask-factories.html" target="_blank" rel="noopener"
 &gt;https://citizen-stig.github.io/2016/02/17/using-celery-with-flask-factories.html&lt;/a&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;经过搜索找到了这篇文章，给了我启发。so，记录下我自己项目的使用方式吧&lt;/p&gt;
&lt;p&gt;baby/__init__.py保持create_app不变&lt;/p&gt;
&lt;p&gt;添加文件&lt;/p&gt;
&lt;p&gt;baby/celery.py文件，代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Author: durban.zhang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Date: 2019-11-13 17:19:11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified by: durban.zhang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified time: 2019-11-13 17:19:25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; celery &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Celery
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_celery&lt;/span&gt;(app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; celery &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Celery(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;import_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; backend&lt;span style="color:#f92672"&gt;=&lt;/span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;CELERY_RESULT_BACKEND&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; broker&lt;span style="color:#f92672"&gt;=&lt;/span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;CELERY_BROKER_URL&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; celery&lt;span style="color:#f92672"&gt;.&lt;/span&gt;conf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ContextTask&lt;/span&gt;(celery&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Task):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__call__&lt;/span&gt;(self, &lt;span style="color:#f92672"&gt;*&lt;/span&gt;args, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;args, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; celery&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Task &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ContextTask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; celery
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后创建baby/task.py文件，代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Author: durban.zhang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Date: 2019-11-13 16:30:07&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified by: durban.zhang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# @Last Modified time: 2019-11-13 17:19:48&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.celery &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_celery
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;celery &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_celery(create_app())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@celery.task&lt;/span&gt;(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;tasks.add_together&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;add_together&lt;/span&gt;(a, b):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; a &lt;span style="color:#f92672"&gt;+&lt;/span&gt; b
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些添加完之后，运行celery&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;celery -A baby.task worker -E --loglevel&lt;span style="color:#f92672"&gt;=&lt;/span&gt;info
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输入类似如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;celery@durbanzhangdeMacBook-Pro v4.3.0 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rhubarb&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Darwin-16.7.0-x86_64-i386-64bit 2019-11-13 17:20:02
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;config&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&amp;gt; app: baby:0x1030c1310
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&amp;gt; transport: redis://localhost:6379//
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&amp;gt; results: redis://localhost:6379/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&amp;gt; concurrency: &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;prefork&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&amp;gt; task events: ON
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&amp;gt; celery exchange&lt;span style="color:#f92672"&gt;=&lt;/span&gt;celery&lt;span style="color:#f92672"&gt;(&lt;/span&gt;direct&lt;span style="color:#f92672"&gt;)&lt;/span&gt; key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;celery
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;tasks&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; . tasks.add_together
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:20:03,547: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Connected to redis://localhost:6379//
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:20:03,561: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; mingle: searching &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; neighbors
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:20:04,617: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; mingle: all alone
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:20:04,649: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; celery@durbanzhangdeMacBook-Pro ready.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Celery相关的参数配置如下（使用的是Redis）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;CELERY_BROKER_URL&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;redis://localhost:6379&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;CELERY_RESULT_BACKEND&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;redis://localhost:6379&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面看下在view中如何调用这个任务吧&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.celery &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_celery
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/together&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;together&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; celery &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_celery(current_app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; celery&lt;span style="color:#f92672"&gt;.&lt;/span&gt;send_task(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;tasks.add_together&amp;#39;&lt;/span&gt;, args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wait())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;success&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我这里使用的是Blueprint，所以只说使用Blueprint的情况&lt;/p&gt;
&lt;p&gt;运行项目，访问http://localhost:5000/stream/together&lt;/p&gt;
&lt;p&gt;看下celery的输出&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:51:47,160: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Received task: tasks.add_together&lt;span style="color:#f92672"&gt;[&lt;/span&gt;b37ca0cc-1fa3-4182-9ca8-3404aafc6ad3&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:51:47,201: INFO/ForkPoolWorker-2&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Task tasks.add_together&lt;span style="color:#f92672"&gt;[&lt;/span&gt;b37ca0cc-1fa3-4182-9ca8-3404aafc6ad3&lt;span style="color:#f92672"&gt;]&lt;/span&gt; succeeded in 0.0225263449999602s: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:52:19,636: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Received task: tasks.add_together&lt;span style="color:#f92672"&gt;[&lt;/span&gt;223f1ead-cbf4-46c3-b5fc-18f362d9a3b5&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:52:19,672: INFO/ForkPoolWorker-1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Task tasks.add_together&lt;span style="color:#f92672"&gt;[&lt;/span&gt;223f1ead-cbf4-46c3-b5fc-18f362d9a3b5&lt;span style="color:#f92672"&gt;]&lt;/span&gt; succeeded in 0.01702427300006093s: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:52:20,395: INFO/MainProcess&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Received task: tasks.add_together&lt;span style="color:#f92672"&gt;[&lt;/span&gt;87b47fde-daae-4a9a-940a-3d038577af0d&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2019-11-13 17:52:20,398: INFO/ForkPoolWorker-2&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Task tasks.add_together&lt;span style="color:#f92672"&gt;[&lt;/span&gt;87b47fde-daae-4a9a-940a-3d038577af0d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; succeeded in 0.0009092729999338189s: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一切运行正常。当然我这边也有一个输出&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;127.0.0.1 - - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;13/Nov/2019 17:52:19&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;GET /stream/together HTTP/1.1&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;160&lt;/span&gt; 0.212275
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里输出的值“5”&lt;/p&gt;
&lt;p&gt;好了，记录到此结束。&lt;/p&gt;</description></item><item><title>Flask 1.0 - Flask MongoEngine 'QuerySet' object has no attribute 'get_or_404'</title><link>https://zhangdapeng.dev/p/flask-1-0-flask-mongoengine-queryset-object-has-no-attribute-get_or_404/</link><pubDate>Tue, 29 Jul 2025 10:56:29 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-flask-mongoengine-queryset-object-has-no-attribute-get_or_404/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 - Flask MongoEngine 'QuerySet' object has no attribute 'get_or_404'" /&gt;&lt;p&gt;学习Flask 1.0中使用“flask-mongoengine==0.9.5”，遇到类似“ AttributeError: &amp;lsquo;QuerySet&amp;rsquo; object has no attribute &amp;lsquo;first_or_404&amp;rsquo;”这个错误&lt;/p&gt;
&lt;p&gt;网上查了文档，说要在创建model类的时候添加一段代码，如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;meta &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;abstract&amp;#39;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;queryset_class&amp;#39;&lt;/span&gt;: BaseQuerySet}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从代码的逻辑上来说，应该不会有问题的，而且Flask 1.0的官方文档应该也不会傻到用错误的实例来误导大家吧&lt;/p&gt;
&lt;p&gt;文档地址：&lt;a class="link" href="https://flask.palletsprojects.com/en/1.1.x/patterns/mongoengine/" target="_blank" rel="noopener"
 &gt;https://flask.palletsprojects.com/en/1.1.x/patterns/mongoengine/&lt;/a&gt;中有个创建Movie Model的代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; mongoengine &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Movie&lt;/span&gt;(me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Document):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StringField(required&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; year &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;IntField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rated &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StringField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; director &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StringField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; actors &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ListField()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;于是去看了“flask-mongoengine==0.9.5”的源代码，不看不知道，一看醒然大悟呀&lt;/p&gt;
&lt;p&gt;在源码文件中“site-packages/flask_mongoengine/__init__.py”这个文件中有Document这个类如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Document&lt;/span&gt;(mongoengine&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Document):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Abstract document with extra helpers in the queryset class&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; meta &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;abstract&amp;#39;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;queryset_class&amp;#39;&lt;/span&gt;: BaseQuerySet}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;paginate_field&lt;/span&gt;(self, field_name, page, per_page, total&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Paginate items within a list field.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# TODO this doesn&amp;#39;t sound useful at all - remove in next release?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; getattr(self, field_name &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_count&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total &lt;span style="color:#f92672"&gt;=&lt;/span&gt; total &lt;span style="color:#f92672"&gt;or&lt;/span&gt; count &lt;span style="color:#f92672"&gt;or&lt;/span&gt; len(getattr(self, field_name))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ListFieldPagination(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;__class__&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects, self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pk, field_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; page, per_page, total&lt;span style="color:#f92672"&gt;=&lt;/span&gt;total)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;于是明白了，官方文档果然有点傻，忽悠我们上当嘞，我将Movie的Model类代码修改下，如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask_mongoengine &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Document
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; mongoengine &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; . &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Imdb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Movie&lt;/span&gt;(Document):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StringField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; year &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;IntField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rated &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StringField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; director &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StringField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; actors &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ListField()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; imdb &lt;span style="color:#f92672"&gt;=&lt;/span&gt; me&lt;span style="color:#f92672"&gt;.&lt;/span&gt;EmbeddedDocumentField(Imdb)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再次运行试试，果然代码没有再报错误了。&lt;/p&gt;
&lt;p&gt;再次进行写入操作后，写入逻辑如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bttf &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Movie(title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Back To The Feature&amp;#39;&lt;/span&gt;, year&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1985&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bttf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;actors &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Michael J. Fox&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Christopher Lloyd&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bttf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;imdb &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Imdb(imdb_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;tt0088763&amp;#39;&lt;/span&gt;, rating&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;8.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bttf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;save()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bttf &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Movie&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects(title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Back To The Feature&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;first_or_404()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;终于可以查到了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; use baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switched to db baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; db.collection
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby.collection
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; coll &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db.baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby.baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; coll.find&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; coll.find&lt;span style="color:#f92672"&gt;({&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;Back To The Feature&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; coll.find&lt;span style="color:#f92672"&gt;({&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;Back To The Feature&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; col &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db.movie
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby.movie
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; col.find&lt;span style="color:#f92672"&gt;({&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;Back To The Feature&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb58d3bff86222392e8ca6&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb5a06e08d79942ea2184b&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb5c0299c8eebebb5f6ce0&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb5c105a4bbdabd53bc2f6&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb5c20fc294890b9693548&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb5f41a6fb86c4f3bc4d4d&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt; : ObjectId&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5dcb5f42a6fb86c4f3bc4d4e&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Back To The Feature&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;year&amp;#34;&lt;/span&gt; : 1985, &lt;span style="color:#e6db74"&gt;&amp;#34;actors&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Michael J. Fox&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Christopher Lloyd&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;imdb&amp;#34;&lt;/span&gt; : &lt;span style="color:#f92672"&gt;{&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;imdb_id&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;tt0088763&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rating&amp;#34;&lt;/span&gt; : 8.5 &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后还发现这个文档这里的一个坑，mongodb的配置如果按照下面的写法&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MONGODB_SETTINGS&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;baby&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会报错提示如下错误&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;MongoEngineConnectionError: You have not defined a default connection&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;改为如下可正常运行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MONGODB_SETTINGS&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;baby&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;alias&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;刚刚又遇到一个问题&lt;/p&gt;
&lt;p&gt;在使用get_or_404()这个方法的时候，居然报404错误，但是如果去掉get_or_404()，直接使用如下代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;movies &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Movie&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects(title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Back To The Feature&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后将数据渲染到前端居然可以获取到数据，是不是说，不需要使用get_or_404()还是说我使用的方法错误了，应该还需要做些什么操作。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;langcode/me.j2&amp;#39;&lt;/span&gt;, movies&lt;span style="color:#f92672"&gt;=&lt;/span&gt;movies)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;渲染的html代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% extends &amp;#34;base.j2&amp;#34; %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block content %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% for movie in movies %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;{{ movie.id }}&amp;lt;/&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;{{ movie.title }}&amp;lt;/&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;{{ movie.year }}&amp;lt;/&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;{{ movie.imdb.rating }}&amp;lt;/&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endfor %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 - AttributeError: 'DispatcherMiddleware' object has no attribute 'config'</title><link>https://zhangdapeng.dev/p/flask-1-0-attributeerror-dispatchermiddleware-object-has-no-attribute-config/</link><pubDate>Tue, 29 Jul 2025 10:56:26 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-attributeerror-dispatchermiddleware-object-has-no-attribute-config/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 - AttributeError: 'DispatcherMiddleware' object has no attribute 'config'" /&gt;&lt;p&gt;Flask 1.0使用过程中遇到“AttributeError: &amp;lsquo;DispatcherMiddleware&amp;rsquo; object has no attribute &amp;lsquo;config&amp;rsquo;”，官方的文档暂时还没更新，这里经过资料搜集，暂时有了一个可使用的解决方案。&lt;/p&gt;
&lt;p&gt;最近在偷偷的自学Flask，一边看文档，一边实践，发现了一些小问题。&lt;/p&gt;
&lt;p&gt;官方文档在“绑定应用”的章节实例代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.middleware.dispatcher &lt;span style="color:#f92672"&gt;import&lt;/span&gt; DispatcherMiddleware
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; frontend_app &lt;span style="color:#f92672"&gt;import&lt;/span&gt; application &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; frontend
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; backend_app &lt;span style="color:#f92672"&gt;import&lt;/span&gt; application &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; backend
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;application &lt;span style="color:#f92672"&gt;=&lt;/span&gt; DispatcherMiddleware(frontend, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/backend&amp;#39;&lt;/span&gt;: backend
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是在经过测试后运行报错了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Server initialized &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; eventlet.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Traceback &lt;span style="color:#f92672"&gt;(&lt;/span&gt;most recent call last&lt;span style="color:#f92672"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; File &lt;span style="color:#e6db74"&gt;&amp;#34;wsgi.py&amp;#34;&lt;/span&gt;, line 12, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; socketio.run&lt;span style="color:#f92672"&gt;(&lt;/span&gt;application, debug&lt;span style="color:#f92672"&gt;=&lt;/span&gt;True&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; File &lt;span style="color:#e6db74"&gt;&amp;#34;/Users/durban/python/baby/.env3_baby/lib/python3.7/site-packages/flask_socketio/__init__.py&amp;#34;&lt;/span&gt;, line 498, in run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; server_name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; app.config&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;SERVER_NAME&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AttributeError: &lt;span style="color:#e6db74"&gt;&amp;#39;DispatcherMiddleware&amp;#39;&lt;/span&gt; object has no attribute &lt;span style="color:#e6db74"&gt;&amp;#39;config&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看出错误的问题属性没有了&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;a class="link" href="https://stackoverflow.com/questions/36219842/flask-app-wrapped-with-dispatchermiddleware-no-longer-has-test-client" target="_blank" rel="noopener"
 &gt;https://stackoverflow.com/questions/36219842/flask-app-wrapped-with-dispatchermiddleware-no-longer-has-test-client&lt;/a&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;于是在这里找到了答案&lt;/p&gt;
&lt;p&gt;最后的实例使用方法如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby_backend &lt;span style="color:#f92672"&gt;import&lt;/span&gt; application &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; backend
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.middleware.dispatcher &lt;span style="color:#f92672"&gt;import&lt;/span&gt; DispatcherMiddleware
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wsgi_app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; DispatcherMiddleware(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wsgi_app, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/backend&amp;#39;&lt;/span&gt;: backend
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的baby_backend是个package&lt;/p&gt;</description></item><item><title>实战记录 - Flask中使用SQLAlchemy创建Model，实现“一对多”、“多对多”的方式</title><link>https://zhangdapeng.dev/p/shi-zhan-ji-lu-flask-zhong-shi-yong-sqlalchemy-chuang-jian-model-shi-xian-yi-dui-duo-duo-dui-duo-de-fang-shi/</link><pubDate>Tue, 29 Jul 2025 10:56:22 +0800</pubDate><guid>https://zhangdapeng.dev/p/shi-zhan-ji-lu-flask-zhong-shi-yong-sqlalchemy-chuang-jian-model-shi-xian-yi-dui-duo-duo-dui-duo-de-fang-shi/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post 实战记录 - Flask中使用SQLAlchemy创建Model，实现“一对多”、“多对多”的方式" /&gt;&lt;p&gt;Flask中使用SQLAlchemy创建Model，实现“一对多”、“多对多”的方式&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Flask==0.12.2 Flask-SQLAlchemy==2.3.2&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="实现一对多的情况"&gt;实现“一对多”的情况
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;一个帖子对应一个分类，一个分类对应多个贴子&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;使用backref的方式如下&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;分类模型&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Category&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39;分类&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __tablename__ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;category&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;), unique&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;Category &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;帖子模型&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Article&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39; 帖子&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __tablename__ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;article&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;自增id&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;), unique&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;标题&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Text, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;描述内容&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;users.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;category.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 这里是关键点 通过使用backref的方式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;relationship(&lt;span style="color:#e6db74"&gt;&amp;#39;Category&amp;#39;&lt;/span&gt;, backref&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;article&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;Article &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;使用back_populates的方式如下&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;分类模型&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Category&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39;分类&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __tablename__ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;category&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;), unique&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 这里是关键点 通过使用backref的方式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; article &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;relationship(&lt;span style="color:#e6db74"&gt;&amp;#39;Article&amp;#39;&lt;/span&gt;, back_populates&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;back_populates&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;Category &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;帖子模型&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Article&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39; 帖子&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __tablename__ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;article&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;自增id&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;), unique&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;标题&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Text, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;描述内容&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;users.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;category.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 这里是关键点 通过使用backref的方式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;relationship(&lt;span style="color:#e6db74"&gt;&amp;#39;Category&amp;#39;&lt;/span&gt;, back_populates&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;article&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;Article &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="实现多对多的情况"&gt;实现“多对多”的情况
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;一个帖子对应多个标签，一个标签对应多个贴子&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;需要建立一个中间表&lt;/p&gt;
&lt;p&gt;帖子标签关联表配置&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ARTICLE_TAGS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Table(&lt;span style="color:#e6db74"&gt;&amp;#39;article_tags&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(&lt;span style="color:#e6db74"&gt;&amp;#39;tag_id&amp;#39;&lt;/span&gt;, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;tags.id&amp;#39;&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(&lt;span style="color:#e6db74"&gt;&amp;#39;article_id&amp;#39;&lt;/span&gt;, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;article.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;帖子模型&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Article&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39;帖子&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __tablename__ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;article&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;自增id&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;), unique&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;标题&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Text, doc&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;描述内容&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;users.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ForeignKey(&lt;span style="color:#e6db74"&gt;&amp;#39;category.id&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 多对多的关联配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tags &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Tag&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; secondary&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ARTICLE_TAGS,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; backref&lt;span style="color:#f92672"&gt;=&lt;/span&gt;db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;backref(&lt;span style="color:#e6db74"&gt;&amp;#39;article&amp;#39;&lt;/span&gt;, lazy&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;dynamic&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;Article &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;标签模型&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Tag&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39;标签&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __tablename__ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, title):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; title
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;ul&gt;
&lt;li&gt;疑问article_tags这个需要建表吗&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;答：当然需要，不然怎么存储多对多的关联呢&lt;/p&gt;

 &lt;/blockquote&gt;</description></item><item><title>Flask 1.0 进阶 - 具有Blueprint的模块化应用程序</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ju-you-blueprint-de-mo-kuai-hua-ying-yong-cheng-xu/</link><pubDate>Tue, 29 Jul 2025 10:56:19 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ju-you-blueprint-de-mo-kuai-hua-ying-yong-cheng-xu/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 具有Blueprint的模块化应用程序" /&gt;&lt;h2 id="具有blueprint的模块化应用程序"&gt;具有Blueprint的模块化应用程序
&lt;/h2&gt;&lt;p&gt;Flask使用蓝图概念来制作应用程序组件，并在应用程序或应用程序中支持常见模式。蓝图可以极大地简化大型应用程序的工作方式，并为Flask扩展提供注册应用程序操作的核心方法。 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Blueprint" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Blueprint&lt;/strong&gt;&lt;/a&gt; 对象与 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; 应用程序对象的工作方式类似，但它实际上并不是一个应用程序。相反，它是如何构建或扩展应用程序的蓝图。&lt;/p&gt;
&lt;h3 id="为何选择蓝图"&gt;为何选择蓝图？
&lt;/h3&gt;&lt;p&gt;Flask中的蓝图适用于以下情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将应用程序纳入一组蓝图中。这是大型应用的理想选择;项目可以实例化一个应用程序对象，初始化几个扩展，并注册一组蓝图。&lt;/li&gt;
&lt;li&gt;在URL前缀和/或子域中的应用程序上注册蓝图。URL前缀/子域中的参数成为蓝图中所有视图函数的公共视图参数（具有默认值）。&lt;/li&gt;
&lt;li&gt;在具有不同URL规则的应用程序上多次注册蓝图。&lt;/li&gt;
&lt;li&gt;通过蓝图提供模板过滤器，静态文件，模板和其他实用程序。蓝图不必实现应用程序或查看功能。&lt;/li&gt;
&lt;li&gt;在初始化Flask扩展时，在应用程序中为任何这些情况注册蓝图。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Flask中的蓝图不是一个可插拔的应用程序，因为它实际上不是一个应用程序 - 它是一组可以在应用程序上注册的操作，甚至多次。为什么没有多个应用程序对象？您可以这样做（请参阅&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/patterns/appdispatch/#app-dispatch" target="_blank" rel="noopener"
 &gt;应用程序调度&lt;/a&gt;），但您的应用程序将具有单独的配置，并将在WSGI层进行管理。&lt;/p&gt;
&lt;p&gt;相反，蓝图在Flask级别提供分离，共享应用程序配置，并且可以根据需要在注册时更改应用程序对象。缺点是，一旦创建应用程序，您无法取消注册蓝图，而不必销毁整个应用程序对象。&lt;/p&gt;
&lt;h3 id="蓝图的概念"&gt;蓝图的概念
&lt;/h3&gt;&lt;p&gt;蓝图的基本概念是它们记录在应用程序上注册时要执行的操作。在分派请求和从一个端点到另一个端点生成URL时，Flask将视图函数与蓝图相关联。&lt;/p&gt;
&lt;h3 id="我的第一份蓝图"&gt;我的第一份蓝图
&lt;/h3&gt;&lt;p&gt;这是一个非常基本的蓝图。在这种情况下，我们希望实现一个简单渲染静态模板的蓝图：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Blueprint, render_template, abort
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; jinja2 &lt;span style="color:#f92672"&gt;import&lt;/span&gt; TemplateNotFound
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;simple_page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Blueprint(&lt;span style="color:#e6db74"&gt;&amp;#39;simple_page&amp;#39;&lt;/span&gt;, __name__,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; template_folder&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;templates&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@simple_page.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, defaults&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@simple_page.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;lt;page&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;show&lt;/span&gt;(page):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;pages/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.html&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; page)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; TemplateNotFound:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; abort(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在&lt;code&gt;@simple_page.route&lt;/code&gt;装饰器的帮助下绑定函数时，蓝图将记录在应用程序稍后注册时在应用程序上注册函数&lt;code&gt;show&lt;/code&gt;的意图。此外，它将为函数的端点添加蓝图的名称，该蓝图的名称是给予 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Blueprint" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Blueprint&lt;/strong&gt;&lt;/a&gt; 构造函数的（在本例中也是&lt;code&gt;simple_page&lt;/code&gt;）。蓝图的名称不会修改URL，只会修改endpoint。&lt;/p&gt;
&lt;h3 id="注册蓝图"&gt;注册蓝图
&lt;/h3&gt;&lt;p&gt;那么你如何注册这个蓝图？像这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; yourapplication.simple_page &lt;span style="color:#f92672"&gt;import&lt;/span&gt; simple_page
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register_blueprint(simple_page)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您检查在应用程序上注册的规则，您会发现：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&amp;lt;Rule &lt;span style="color:#e6db74"&gt;&amp;#39;/static/&amp;lt;filename&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;HEAD, OPTIONS, GET&lt;span style="color:#f92672"&gt;)&lt;/span&gt; -&amp;gt; static&amp;gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Rule &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;lt;page&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;HEAD, OPTIONS, GET&lt;span style="color:#f92672"&gt;)&lt;/span&gt; -&amp;gt; simple_page.show&amp;gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Rule &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;HEAD, OPTIONS, GET&lt;span style="color:#f92672"&gt;)&lt;/span&gt; -&amp;gt; simple_page.show&amp;gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个显然来自应用程序本身的静态文件。另外两个用于&lt;code&gt;simple_page&lt;/code&gt;蓝图的 &lt;em&gt;show&lt;/em&gt; 函数。如您所见，它们还以蓝图的名称为前缀，并以点（&lt;code&gt;.&lt;/code&gt;）分隔。&lt;/p&gt;
&lt;p&gt;然而，蓝图也可以安装在不同的位置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register_blueprint(simple_page, url_prefix&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;/pages&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后，生成的规则对应如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&amp;lt;Rule &lt;span style="color:#e6db74"&gt;&amp;#39;/static/&amp;lt;filename&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;HEAD, OPTIONS, GET&lt;span style="color:#f92672"&gt;)&lt;/span&gt; -&amp;gt; static&amp;gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Rule &lt;span style="color:#e6db74"&gt;&amp;#39;/pages/&amp;lt;page&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;HEAD, OPTIONS, GET&lt;span style="color:#f92672"&gt;)&lt;/span&gt; -&amp;gt; simple_page.show&amp;gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Rule &lt;span style="color:#e6db74"&gt;&amp;#39;/pages/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;HEAD, OPTIONS, GET&lt;span style="color:#f92672"&gt;)&lt;/span&gt; -&amp;gt; simple_page.show&amp;gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最重要的是，您可以多次注册蓝图，但并非每个蓝图都可以正确响应。实际上，如果蓝图可以多次安装，它取决于蓝图的实现方式。&lt;/p&gt;
&lt;h3 id="蓝图资源"&gt;蓝图资源
&lt;/h3&gt;&lt;p&gt;蓝图也可以提供资源。有时您可能只想为其提供的资源引入蓝图。&lt;/p&gt;
&lt;h3 id="蓝图资源文件夹"&gt;蓝图资源文件夹
&lt;/h3&gt;&lt;p&gt;与常规应用程序一样，蓝图被认为包含在文件夹中。虽然多个蓝图可以来自同一个文件夹，但并非必须如此，通常不建议这样做。&lt;/p&gt;
&lt;p&gt;该文件夹是从 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Blueprint" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Blueprint&lt;/strong&gt;&lt;/a&gt; 的第二个参数推断出来的，通常是*&lt;strong&gt;name&lt;/strong&gt;*。此参数指定哪个逻辑Python模块或包对应于蓝图。如果它指向一个实际的Python包，那么包（它是文件系统上的文件夹）就是资源文件夹。如果它是一个模块，则包含模块的包将是资源文件夹。您可以访问 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Blueprint.root_path" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Blueprint.root_path&lt;/strong&gt;&lt;/a&gt; 属性以查看资源文件夹是什么：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; simple_page.root_path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;/Users/username/TestProject/yourapplication&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要从此文件夹快速打开源，您可以使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Blueprint.open_resource" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;open_resource()&lt;/strong&gt;&lt;/a&gt; 函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; simple_page&lt;span style="color:#f92672"&gt;.&lt;/span&gt;open_resource(&lt;span style="color:#e6db74"&gt;&amp;#39;static/style.css&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="静态文件"&gt;静态文件
&lt;/h3&gt;&lt;p&gt;蓝图可以通过使用&lt;code&gt;static_folder&lt;/code&gt;参数提供文件系统上文件夹的路径来公开具有静态文件的文件夹。它是绝对路径或相对于蓝图的位置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;admin &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Blueprint(&lt;span style="color:#e6db74"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;, __name__, static_folder&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;static&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;默认情况下，路径的最右边部分是它在Web上公开的位置。可以使用&lt;code&gt;static_url_path&lt;/code&gt;参数更改此设置。因为此处的文件夹名为&lt;code&gt;static&lt;/code&gt;，所以它将在蓝图+&lt;code&gt;/static&lt;/code&gt;的&lt;code&gt;url_prefix&lt;/code&gt;中提供。如果蓝图具有前缀&lt;code&gt;/admin&lt;/code&gt;，则静态URL将为&lt;code&gt;/admin/static&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;endpoint名为&lt;code&gt;blueprint_name.static&lt;/code&gt;。您可以使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;url_for()&lt;/strong&gt;&lt;/a&gt; 生成URL，就像使用应用程序的静态文件夹一样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;admin.static&amp;#39;&lt;/span&gt;, filename&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;style.css&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是，如果蓝图没有&lt;code&gt;url_prefix&lt;/code&gt;，则无法访问蓝图的静态文件夹。这是因为在这种情况下URL将是&lt;code&gt;/static&lt;/code&gt;，并且应用程序的&lt;code&gt;/static&lt;/code&gt;路由优先。与模板文件夹不同，如果应用程序静态文件夹中不存在该文件，则不会搜索蓝图静态文件夹。&lt;/p&gt;
&lt;h3 id="模板"&gt;模板
&lt;/h3&gt;&lt;p&gt;如果您希望蓝图公开模板，可以通过向Blueprint构造函数提供&lt;em&gt;template_folder&lt;/em&gt;参数来实现：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;admin &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Blueprint(&lt;span style="color:#e6db74"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;, __name__, template_folder&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;templates&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于静态文件，路径可以是蓝图资源文件夹的绝对路径或相对路径。&lt;/p&gt;
&lt;p&gt;模板文件夹添加到模板的搜索路径中，但优先级低于实际应用程序的模板文件夹。这样，您可以轻松覆盖蓝图在实际应用程序中提供的模板。这也意味着如果您不希望意外覆盖蓝图模板，请确保没有其他蓝图或实际应用程序模板具有相同的相对路径。当多个蓝图提供相同的相对模板路径时，注册的第一个蓝图优先于其他蓝图。&lt;/p&gt;
&lt;p&gt;因此，如果您在&lt;code&gt;yourapplication/admin&lt;/code&gt;中的文件夹中有蓝图，并且您希望呈现模板&lt;code&gt;'admin/index.html'&lt;/code&gt;并且您提供了&lt;code&gt;templates&lt;/code&gt;作为&lt;em&gt;template_folder&lt;/em&gt;，则必须创建如下文件：&lt;code&gt;yourapplication/admin/templates/admin/index.html&lt;/code&gt;的。额外&lt;code&gt;admin&lt;/code&gt;文件夹的原因是为了避免在实际应用程序模板文件夹中使用名为&lt;code&gt;index.html&lt;/code&gt;的模板覆盖我们的模板。&lt;/p&gt;
&lt;p&gt;为了进一步重申这一点：如果你有一个名为&lt;code&gt;admin&lt;/code&gt;的蓝图，并且你想渲染一个名为&lt;code&gt;index.html&lt;/code&gt;的模板，这个模板是特定于这个蓝图的，那么最好的想法就是像这样布置你的模板：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yourpackage/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; blueprints/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; admin/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; templates/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; admin/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后，当您想要渲染模板时，使用&lt;code&gt;admin/index.html&lt;/code&gt;作为名称来查找模板。如果您在加载正确的模板时遇到问题，请启用&lt;code&gt;EXPLAIN_TEMPLATE_LOADING&lt;/code&gt;配置变量，该变量将指示Flask打印出在每次&lt;code&gt;render_template&lt;/code&gt;调用时定位模板的步骤。&lt;/p&gt;
&lt;h3 id="构建网址"&gt;构建网址
&lt;/h3&gt;&lt;p&gt;如果你想从一个页面链接到另一个页面，你可以使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;url_for()&lt;/strong&gt;&lt;/a&gt; 函数，就像你通常只需要在URL端点前面加上蓝图的名称和点（&lt;code&gt;.&lt;/code&gt;）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;admin.index&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此外，如果您处于蓝图或渲染模板的视图功能中并且想要链接到同一蓝图的另一个端点，则可以通过在端点前面添加一个点来使用相对重定向：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;.index&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将链接到&lt;code&gt;admin.index&lt;/code&gt;，例如，以防当前请求被分派到任何其他管理蓝图端点。&lt;/p&gt;
&lt;h3 id="错误处理程序"&gt;错误处理程序
&lt;/h3&gt;&lt;p&gt;蓝图支持&lt;code&gt;errorhandler&lt;/code&gt;装饰器，就像 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; 应用程序对象一样，因此很容易制作特定于蓝图的自定义错误页面。&lt;/p&gt;
&lt;p&gt;以下是“404找不到页面”异常的示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@simple_page.errorhandler&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;page_not_found&lt;/span&gt;(e):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;pages/404.html&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;大多数错误处理程序将按预期工作;但是，有关404和405例外处理程序的警告。这些错误处理程序仅从相应的&lt;code&gt;raise&lt;/code&gt;语句或另一个蓝图视图函数中的&lt;code&gt;abort&lt;/code&gt;调用中调用;例如，无效的URL访问不会调用它们。这是因为蓝图不“拥有”某个URL空间，因此如果给定无效的URL，应用程序实例无法知道它应该运行哪个蓝图错误处理程序。如果您希望基于URL前缀对这些错误执行不同的处理策略，可以使用请求代理对象在应用程序级别定义它们：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.errorhandler&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.errorhandler&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;405&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_handle_api_error&lt;/span&gt;(ex):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;startswith(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; jsonify_error(ex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ex
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有关错误处理的更多信息请参阅&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/patterns/errorpages/#errorpages" target="_blank" rel="noopener"
 &gt;自定义错误页&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 进阶 - 请求上下文</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-qing-qiu-shang-xia-wen/</link><pubDate>Tue, 29 Jul 2025 10:56:14 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-qing-qiu-shang-xia-wen/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 请求上下文" /&gt;&lt;p&gt;请求上下文在请求期间跟踪请求级数据。而不是将请求对象传递给在请求期间运行的每个函数，而是访问 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request&lt;/strong&gt;&lt;/a&gt; 和 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;会话&lt;/strong&gt;&lt;/a&gt; 代理。&lt;/p&gt;
&lt;p&gt;这类似于应用程序上下文，它独立于请求跟踪应用程序级数据。当推送请求上下文时，推送相应的应用程序上下文。&lt;/p&gt;
&lt;h3 id="上下文的目的"&gt;上下文的目的
&lt;/h3&gt;&lt;p&gt;当 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; 应用程序处理请求时，它会根据从WSGI服务器收到的环境创建一个 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Request&lt;/strong&gt;&lt;/a&gt; 对象。由于&lt;em&gt;worker&lt;/em&gt;（线程，进程或协程取决于服务器）一次只处理一个请求，因此在该请求期间可以将请求数据视为该工作者的全局请求。Flask为此使用术语&lt;em&gt;context local&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;Flask在处理请求时自动推送请求上下文。查看函数，错误处理程序以及在请求期间运行的其他函数将有权访问 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request&lt;/strong&gt;&lt;/a&gt; 代理，该代理指向当前请求的请求对象。&lt;/p&gt;
&lt;h3 id="上下文的生命周期"&gt;上下文的生命周期
&lt;/h3&gt;&lt;p&gt;当Flask应用程序开始处理请求时，它会push请求上下文，这也会push &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/appcontext/" target="_blank" rel="noopener"
 &gt;应用程序上下文&lt;/a&gt; 。当请求结束时，它会pop请求上下文，然后pop应用程序上下文。&lt;/p&gt;
&lt;p&gt;上下文对于每个线程（或其他worker类型）是唯一的。&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request" target="_blank" rel="noopener"
 &gt;request&lt;/a&gt; 无法传递给另一个线程，另一个线程将具有不同的上下文堆栈，并且不会知道父线程指向的请求。&lt;/p&gt;
&lt;p&gt;上下文本地在Werkzeug中实现。有关其内部工作原理的更多信息，请参阅&lt;a class="link" href="https://werkzeug.palletsprojects.com/en/0.15.x/local/" target="_blank" rel="noopener"
 &gt;上下文本地&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="手动推送上下文"&gt;手动推送上下文
&lt;/h3&gt;&lt;p&gt;如果您尝试在请求上下文之外访问请求或使用它的任何内容，您将收到以下错误消息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RuntimeError: Working outside of request context.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;This typically means that you attempted to use functionality that
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;needed an active HTTP request. Consult the documentation on testing
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; information about how to avoid this problem.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通常只有在测试需要active请求的代码时才会发生这种情况。一种选择是使用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.test_client" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;test client&lt;/strong&gt;&lt;/a&gt; 来模拟完整请求。或者，您可以在with块中使用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.test_request_context" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;test_request_context()&lt;/strong&gt;&lt;/a&gt; ，并且块中运行的所有内容都可以访问&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request" target="_blank" rel="noopener"
 &gt;请求&lt;/a&gt; ，并填充测试数据。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;generate_report&lt;/span&gt;(year):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; format &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;format&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/make_report/2017&amp;#39;&lt;/span&gt;, data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;format&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;short&amp;#39;&lt;/span&gt;}):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; generate_report()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您在代码中的其他位置看到与测试无关的错误，则很可能表示您应该将该代码移动到视图函数中。&lt;/p&gt;
&lt;p&gt;有关如何使用交互式Python shell中的请求上下文的信息，请参阅&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/shell/" target="_blank" rel="noopener"
 &gt;使用命令行管理程序&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="上下文如何运作"&gt;上下文如何运作
&lt;/h3&gt;&lt;p&gt;调用&lt;a class="link" href="http://localhost:6419/" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask.wsgi_app()&lt;/strong&gt;&lt;/a&gt;方法来处理每个请求。它在请求期间管理上下文。在内部，请求和应用程序上下文用作堆栈，&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask._request_ctx_stack" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;_request_ctx_stack&lt;/strong&gt;&lt;/a&gt; 和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask._app_ctx_stack" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;_app_ctx_stack&lt;/strong&gt;&lt;/a&gt; 。当上下文被压入堆栈时，依赖于它们的代理是可用的，并指向来自堆栈顶部上下文的信息&lt;/p&gt;
&lt;p&gt;当请求开始时，创建并pushed &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.ctx.RequestContext" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;RequestContext&lt;/strong&gt;&lt;/a&gt;，如果该应用程序的上下文不是顶层上下文，则首先创建并pushes &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.ctx.AppContext" target="_blank" rel="noopener"
 &gt;AppContext&lt;/a&gt;。在推送这些上下文时，&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;current_app&lt;/a&gt;，&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.g" target="_blank" rel="noopener"
 &gt;g&lt;/a&gt;，&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request" target="_blank" rel="noopener"
 &gt;request&lt;/a&gt;和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.session" target="_blank" rel="noopener"
 &gt;session&lt;/a&gt;代理可用于处理请求的原始线程。&lt;/p&gt;
&lt;p&gt;因为上下文是堆栈，所以可以推送其他上下文以在请求期间更改代理。虽然这不是一种常见的模式，但它可以在高级应用程序中使用，例如，进行内部重定向或将不同的应用程序链接在一起。&lt;/p&gt;
&lt;p&gt;在调度请求并生成并发送响应之后，将poped请求上下文，然后popped应用程序上下文。在popped它们之前，执行&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;teardown_request()&lt;/strong&gt;&lt;/a&gt; 和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_appcontext" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;teardown_appcontext()&lt;/strong&gt;&lt;/a&gt; 函数。即使在发送期间发生未处理的异常，它们也会执行。&lt;/p&gt;
&lt;h3 id="回调和错误"&gt;回调和错误
&lt;/h3&gt;&lt;p&gt;Flask在多个阶段调度请求，这可能会影响请求，响应以及如何处理错误。在所有这些阶段中，上下文都是活跃的。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Blueprint" target="_blank" rel="noopener"
 &gt;Blueprint&lt;/a&gt; 可以为这些特定于blueprint的事件添加处理程序。如果blueprint拥有与请求匹配的路由，则将运行blueprint处理程序。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在每个请求之前，调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt; 函数。如果其中一个函数返回一个值，则跳过其他函数。返回值被视为响应，并且不调用视图函数。&lt;/li&gt;
&lt;li&gt;如果&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt;函数未返回响应，则调用匹配路由的视图函数并返回响应。&lt;/li&gt;
&lt;li&gt;视图的返回值将转换为实际响应对象并传递给&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.after_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;after_request()&lt;/strong&gt;&lt;/a&gt;函数。每个函数都返回一个修改的或新的响应对象。&lt;/li&gt;
&lt;li&gt;返回响应后，弹出上下文，调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;teardown_request()&lt;/strong&gt;&lt;/a&gt;和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_appcontext" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;teardown_appcontext()&lt;/strong&gt;&lt;/a&gt;函数。即使在上面的任何点引发了未处理的异常，也会调用这些函数。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果在拆卸函数之前引发异常，Flask会尝试将其与&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.errorhandler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;errorhandler()&lt;/strong&gt;&lt;/a&gt;函数匹配以处理异常并返回响应。如果未找到错误处理程序，或者处理程序本身引发异常，Flask将返回通用的&lt;code&gt;500内部服务器错误&lt;/code&gt;响应。仍然会调用拆卸函数，并传递异常对象。&lt;/p&gt;
&lt;p&gt;如果启用了调试模式，则未处理的异常不会转换为&lt;code&gt;500&lt;/code&gt;响应，而是传播到WSGI服务器。这允许开发服务器向交互式调试器提供回溯。&lt;/p&gt;
&lt;h3 id="teardown回调"&gt;Teardown回调
&lt;/h3&gt;&lt;p&gt;teardown回调与请求分派无关，而是在popped时由上下文调用。即使在调度期间存在未处理的异常，也会调用函数，以及手动推送的上下文。这意味着无法保证请求调度的任何其他部分首先运行。请务必以不依赖于其他回调的方式编写这些函数，并且不会失败。&lt;/p&gt;
&lt;p&gt;在测试期间，在请求结束后推迟popped上下文可能很有用，这样可以在测试函数中访问它们的数据。使用test_client()作为with块来保留上下文，直到with块退出。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask, request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;during view&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.teardown_request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;show_teardown&lt;/span&gt;(exception):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;after with block&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;during with block&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# teardown functions are called after the context with block exits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# the contexts are not popped even though the request ended&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# the contexts are popped and teardown functions are called after&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# the client with block exists&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="signals"&gt;Signals
&lt;/h3&gt;&lt;p&gt;如果&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.signals.signals_available" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;signals_available&lt;/strong&gt;&lt;/a&gt;为true，则发送以下信号：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_started" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request_started&lt;/strong&gt;&lt;/a&gt; 在调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt; 函数之前发送。&lt;/li&gt;
&lt;li&gt;在调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.after_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;after_request()&lt;/strong&gt;&lt;/a&gt;函数之后发送&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_finished" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request_finished&lt;/strong&gt;&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;在开始处理异常时，但在查找或调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.errorhandler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;errorhandler()&lt;/strong&gt;&lt;/a&gt; 之前发送&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.got_request_exception" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;got_request_exception&lt;/strong&gt;&lt;/a&gt; 。&lt;/li&gt;
&lt;li&gt;在调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_request" target="_blank" rel="noopener"
 &gt;teardown_request()&lt;/a&gt; 函数之后发送&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_tearing_down" target="_blank" rel="noopener"
 &gt;request_tearing_down&lt;/a&gt; 。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="上下文保留错误"&gt;上下文保留错误
&lt;/h3&gt;&lt;p&gt;在请求结束时，将popped请求上下文，并销毁与其关联的所有数据。如果在开发过程中发生错误，则延迟销毁数据以进行调试非常有用。&lt;/p&gt;
&lt;p&gt;当开发服务器以开发模式运行时（&lt;code&gt;FLASK_ENV&lt;/code&gt;环境变量设置为&lt;code&gt;'development'&lt;/code&gt;），错误和数据将被保留并显示在交互式调试器中。&lt;/p&gt;
&lt;p&gt;可以使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#PRESERVE_CONTEXT_ON_EXCEPTION" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;PRESERVE_CONTEXT_ON_EXCEPTION&lt;/strong&gt;&lt;/a&gt; 配置控制此行为。如上所述，它在开发环境中默认为&lt;code&gt;True&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;不要在生产中启用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#PRESERVE_CONTEXT_ON_EXCEPTION" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;PRESERVE_CONTEXT_ON_EXCEPTION&lt;/strong&gt;&lt;/a&gt; ，因为它会导致应用程序在异常时泄漏内存。&lt;/p&gt;
&lt;h3 id="关于代理的注意事项"&gt;关于代理的注意事项
&lt;/h3&gt;&lt;p&gt;Flask提供的一些对象是其他对象的代理。对于每个工作线程，代理以相同的方式访问，但指向此页面中描述的绑定到幕后每个worker的唯一对象。&lt;/p&gt;
&lt;p&gt;大多数情况下你不必关心它，但有一些例外情况，知道这个对象是一个真正的代理是很好的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代理对象不能伪造其类型作为实际对象类型。如果要执行实例检查，则必须对要代理的对象执行此操作。&lt;/li&gt;
&lt;li&gt;如果特定对象引用很重要，例如发送信号或将数据传递给后台线程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果需要访问代理的基础对象，请使用&lt;a class="link" href="https://werkzeug.palletsprojects.com/en/0.15.x/local/#werkzeug.local.LocalProxy._get_current_object" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;_get_current_object()&lt;/strong&gt;&lt;/a&gt;方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_get_current_object()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;my_signal&lt;span style="color:#f92672"&gt;.&lt;/span&gt;send(app)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 进阶 - 应用程序上下文</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ying-yong-cheng-xu-shang-xia-wen/</link><pubDate>Tue, 29 Jul 2025 10:56:08 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ying-yong-cheng-xu-shang-xia-wen/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 应用程序上下文" /&gt;&lt;p&gt;应用程序上下文在请求，CLI命令或其他活动期间跟踪应用程序级数据。而不是将应用程序传递给每个函数，而是访问&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;g&lt;/strong&gt;&lt;/a&gt; 代理。&lt;/p&gt;
&lt;p&gt;这类似于&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/reqcontext/" target="_blank" rel="noopener"
 &gt;请求上下文&lt;/a&gt; ，它在请求期间跟踪请求级数据。当推送请求上下文时，推送相应的应用程序上下文。&lt;/p&gt;
&lt;h3 id="上下文的目的"&gt;上下文的目的
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; 应用程序对象具有诸如&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.config" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;config&lt;/strong&gt;&lt;/a&gt; 之类的属性，这些属性对于在视图和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/cli/" target="_blank" rel="noopener"
 &gt;CLI命令&lt;/a&gt; 中进行访问很有用。但是，在项目中的模块中导入&lt;code&gt;app&lt;/code&gt;实例很容易出现循环导入问题。使用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/patterns/appfactories/" target="_blank" rel="noopener"
 &gt;应用工厂模式&lt;/a&gt; 或编写可重复使用的&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/blueprints/" target="_blank" rel="noopener"
 &gt;蓝图&lt;/a&gt; 或&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/extensions/" target="_blank" rel="noopener"
 &gt;扩展&lt;/a&gt; 时，根本不会导入&lt;code&gt;app&lt;/code&gt;实例。&lt;/p&gt;
&lt;p&gt;Flask使用 &lt;em&gt;应用程序上下文&lt;/em&gt; 解决了这个问题。您可以使用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 代理，而不是直接引用应用程序，该代理指向处理当前活动的应用程序。&lt;/p&gt;
&lt;p&gt;Flask在处理请求时自动 &lt;em&gt;推送&lt;/em&gt; 应用程序上下文。视图函数，错误处理程序以及在请求期间运行的其他函数将具有对 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 的访问权限。&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;@app.cli.command()&lt;/code&gt;运行在 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.cli" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask.cli&lt;/strong&gt;&lt;/a&gt; 中注册的CLI命令时，Flask还会自动推送应用程序上下文。&lt;/p&gt;
&lt;h3 id="上下文的生命周期"&gt;上下文的生命周期
&lt;/h3&gt;&lt;p&gt;必要时创建并销毁应用程序上下文。当Flask应用程序开始处理请求时，它会pushes应用程序上下文和&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/reqcontext/" target="_blank" rel="noopener"
 &gt;请求上下文&lt;/a&gt; 。当请求结束时，它会pops请求上下文，然后pops应用程序上下文。通常，应用程序上下文与请求具有相同的生命周期。&lt;/p&gt;
&lt;p&gt;有关上下文如何工作以及请求的完整生命周期的更多信息，请参阅&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/reqcontext/" target="_blank" rel="noopener"
 &gt;请求上下文&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="手动push一个上下文"&gt;手动Push一个上下文
&lt;/h3&gt;&lt;p&gt;如果您尝试在应用程序上下文之外访问&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 或使用它的任何内容，您将收到以下错误消息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RuntimeError: Working outside of application context.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;This typically means that you attempted to use functionality that
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;needed to interface with the current application object in some way.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;To solve this, set up an application context with app.app_context&lt;span style="color:#f92672"&gt;()&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果在配置应用程序时发现错误，例如初始化扩展时，您可以手动push上下文，因为您可以直接访问&lt;code&gt;app&lt;/code&gt;。在&lt;code&gt;with&lt;/code&gt;块中使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.app_context" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app_context()&lt;/strong&gt;&lt;/a&gt; ，块中运行的所有内容都可以访问 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; init_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您在代码中的其他位置看到与配置应用程序无关的错误，则很可能表示您应将该代码移动到视图函数或CLI命令中。&lt;/p&gt;
&lt;h3 id="存储数据"&gt;存储数据
&lt;/h3&gt;&lt;p&gt;应用程序上下文是在请求或CLI命令期间存储公共数据的好地方。Flask为此提供了&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;g object&lt;/strong&gt;&lt;/a&gt; 。它是一个简单的命名空间对象，其生命周期与应用程序上下文相同。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;h3 id="注意"&gt;注意：
&lt;/h3&gt;&lt;p&gt;g名称代表“全局”，但这指的是在上下文中是全局的数据。上下文结束后g上的数据丢失，并且它不是在请求之间存储数据的合适位置。使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;会话(session)&lt;/strong&gt;&lt;/a&gt; 或数据库跨请求存储数据。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;g的常见用途是在请求期间管理资源。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果资源X不存在，则&lt;code&gt;get_X()&lt;/code&gt;创建资源&lt;code&gt;X&lt;/code&gt;，将其缓存为&lt;code&gt;g.X&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;如果资源存在，&lt;code&gt;teardown_X()&lt;/code&gt;将关闭或以其他方式释放资源。它被注册为 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_appcontext" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;teardown_appcontext()&lt;/strong&gt;&lt;/a&gt; 处理程序。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如，您可以使用此模式管理数据库连接：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; g
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_db&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; g:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; connect_to_database()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.teardown_appcontext&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;teardown_db&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pop(&lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; db &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在请求期间，每次调用&lt;code&gt;get_db()&lt;/code&gt;都将返回相同的连接，并在请求结束时自动关闭。&lt;/p&gt;
&lt;p&gt;您可以使用 &lt;a class="link" href="https://werkzeug.palletsprojects.com/en/0.15.x/local/#werkzeug.local.LocalProxy" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;LocalProxy&lt;/strong&gt;&lt;/a&gt; 从&lt;code&gt;get_db()&lt;/code&gt;创建一个新的本地上下文：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.local &lt;span style="color:#f92672"&gt;import&lt;/span&gt; LocalProxy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; LocalProxy(get_db)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;访问&lt;code&gt;db&lt;/code&gt;将在内部调用&lt;code&gt;get_db&lt;/code&gt;，方法与 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 的工作方式相同。&lt;/p&gt;
&lt;p&gt;如果您正在编写扩展名，则应为用户代码保留 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;g&lt;/strong&gt;&lt;/a&gt; 。您可以将内部数据存储在上下文中，但请确保使用足够唯一的名称。使用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask._app_ctx_stack" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;_app_ctx_stack.top&lt;/strong&gt;&lt;/a&gt; 访问当前上下文。有关更多信息，请参阅&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/extensiondev/" target="_blank" rel="noopener"
 &gt;Flask Extension Development&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="事件和信号"&gt;事件和信号
&lt;/h3&gt;&lt;p&gt;当弹出应用程序上下文时，应用程序将调用&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.teardown_appcontext" target="_blank" rel="noopener"
 &gt;teardown_appcontext()&lt;/a&gt; 注册的函数。&lt;/p&gt;</description></item><item><title>Flask 1.0 进阶 - 可插拔视图（Pluggable Views）</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ke-cha-ba-shi-tu-pluggable-views/</link><pubDate>Tue, 29 Jul 2025 10:56:05 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ke-cha-ba-shi-tu-pluggable-views/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 可插拔视图（Pluggable Views）" /&gt;&lt;p&gt;Flask 0.7引入了可插拔视图，其灵感来自Django的通用视图，它们基于类而不是函数。主要目的是您可以替换部分实现，这种方式具有可自定义的可插拔视图。&lt;/p&gt;
&lt;h3 id="基本原则"&gt;基本原则
&lt;/h3&gt;&lt;p&gt;假设您有一个函数从数据库加载对象列表并呈现到模板中：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;show_users&lt;/span&gt;(page):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; users &lt;span style="color:#f92672"&gt;=&lt;/span&gt; User&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;users.html&amp;#39;&lt;/span&gt;, users&lt;span style="color:#f92672"&gt;=&lt;/span&gt;users)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是简单而灵活的，但如果您想以通用方式提供此视图，以便适应其他模型和模板，您可能需要更多灵活性。这是可插入的基于类的视图的地方。作为将此转换为基于类的视图的第一步，您将执行此操作：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.views &lt;span style="color:#f92672"&gt;import&lt;/span&gt; View
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ShowUsers&lt;/span&gt;(View):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch_request&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; users &lt;span style="color:#f92672"&gt;=&lt;/span&gt; User&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;users.html&amp;#39;&lt;/span&gt;, objects&lt;span style="color:#f92672"&gt;=&lt;/span&gt;users)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ShowUsers&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(&lt;span style="color:#e6db74"&gt;&amp;#39;show_users&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;正如您所看到的，您要做的是创建 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.View" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;flask.views.View&lt;/strong&gt;&lt;/a&gt; 的子类并实现 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.View.dispatch_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;dispatch_request()&lt;/strong&gt;&lt;/a&gt; 。然后我们必须使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.View.as_view" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;as_view()&lt;/strong&gt;&lt;/a&gt; 类方法将该类转换为实际的视图函数。传递给该函数的字符串是视图随后具有的端点的名称。但这本身并没有用，所以让我们稍微重构一下代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.views &lt;span style="color:#f92672"&gt;import&lt;/span&gt; View
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ListView&lt;/span&gt;(View):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_template_name&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NotImplementedError&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;render_template&lt;/span&gt;(self, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_template_name(), &lt;span style="color:#f92672"&gt;**&lt;/span&gt;context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch_request&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;objects&amp;#39;&lt;/span&gt;: self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_objects()}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;render_template(context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserView&lt;/span&gt;(ListView):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_template_name&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;users.html&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_objects&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; User&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这当然对这样一个小例子没那么有用，但它足以解释基本原理。当你有一个基于类的视图时，问题出现在&lt;code&gt;self&lt;/code&gt;指向的位置。这种方式的工作方式是每当调度请求时，都会创建一个新的类实例，并使用URL规则中的参数调用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.View.dispatch_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;dispatch_request()&lt;/strong&gt;&lt;/a&gt; 方法。使用传递给 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.View.as_view" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;as_view()&lt;/strong&gt;&lt;/a&gt; 函数的参数来实例化类本身。例如，你可以写一个这样的类：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RenderTemplateView&lt;/span&gt;(View):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, template_name):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;template_name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; template_name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch_request&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;template_name)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后你可以像这样注册：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/about&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;RenderTemplateView&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;about_page&amp;#39;&lt;/span&gt;, template_name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;about.html&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="方法提示"&gt;方法提示
&lt;/h3&gt;&lt;p&gt;通过使用route()或更好的add_url_rule()，可插拔视图像常规函数一样附加到应用程序。但是，这也意味着您必须在附加此视图时提供视图支持的HTTP方法的名称。为了将该信息移动到类，您可以提供具有以下信息的方法属性：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyView&lt;/span&gt;(View):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; methods &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch_request&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/myview&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;MyView&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(&lt;span style="color:#e6db74"&gt;&amp;#39;myview&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="基于方法的调度"&gt;基于方法的调度
&lt;/h3&gt;&lt;p&gt;对于RESTful API，为每个HTTP方法执行不同的功能特别有用。使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.MethodView" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;flask.views.MethodView&lt;/strong&gt;&lt;/a&gt; ，您可以轻松地做到这一点。每个HTTP方法都映射到一个具有相同名称的函数（只是小写）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.views &lt;span style="color:#f92672"&gt;import&lt;/span&gt; MethodView
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserAPI&lt;/span&gt;(MethodView):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; users &lt;span style="color:#f92672"&gt;=&lt;/span&gt; User&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;post&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; User&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_form_data(request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;UserAPI&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(&lt;span style="color:#e6db74"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样你就不必提供methods属性。它会根据类中定义的方法自动设置的。&lt;/p&gt;
&lt;h3 id="装饰视图"&gt;装饰视图
&lt;/h3&gt;&lt;p&gt;由于视图类本身不是添加到路由系统的视图函数，因此装饰类本身没有多大意义。相反，你要么必须手工装饰&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.View.as_view" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;as_view()&lt;/strong&gt;&lt;/a&gt;的返回值：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user_required&lt;/span&gt;(f):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Checks whether user is logged in or raises error 401.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;decorator&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;args, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; abort(&lt;span style="color:#ae81ff"&gt;401&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; f(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;args, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; decorator
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;view &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user_required(UserAPI&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(&lt;span style="color:#e6db74"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;view)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从Flask 0.8开始，还有另一种方法可以指定要在类声明中应用的装饰器列表：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserAPI&lt;/span&gt;(MethodView):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; decorators &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [user_required]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于来自调用者视角的隐式&lt;code&gt;self&lt;/code&gt;，您不能在视图的各个方法上使用常规视图装饰器，但请记住这一点。&lt;/p&gt;
&lt;h3 id="api的方法视图"&gt;API的方法视图
&lt;/h3&gt;&lt;p&gt;Web API通常与HTTP谓词密切配合，因此基于 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.MethodView" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;MethodView&lt;/strong&gt;&lt;/a&gt; 实现这样的API非常有意义。也就是说，您会注意到API将需要不同的URL规则，这些规则在大多数情况下都会转到相同的方法视图。例如，考虑您在Web上公开用户对象：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;URL&lt;/th&gt;
					&lt;th&gt;Method&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;/users/&lt;/td&gt;
					&lt;td&gt;GET&lt;/td&gt;
					&lt;td&gt;Gives a list of all users&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;/users/&lt;/td&gt;
					&lt;td&gt;POST&lt;/td&gt;
					&lt;td&gt;Creates a new user&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;/users/&lt;/td&gt;
					&lt;td&gt;GET&lt;/td&gt;
					&lt;td&gt;Shows a single user&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;/users/&lt;/td&gt;
					&lt;td&gt;PUT&lt;/td&gt;
					&lt;td&gt;Updates a single user&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;/users/&lt;/td&gt;
					&lt;td&gt;DELETE&lt;/td&gt;
					&lt;td&gt;Deletes a single user&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;那么你将如何使用 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.views.MethodView" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;MethodView&lt;/strong&gt;&lt;/a&gt; 做到这一点？诀窍是利用您可以为同一视图提供多个规则的事实。&lt;/p&gt;
&lt;p&gt;我们暂时假设视图看起来像这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserAPI&lt;/span&gt;(MethodView):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(self, user_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; user_id &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# return a list of users&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# expose a single user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;post&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# create a new user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delete&lt;/span&gt;(self, user_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# delete a single user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;put&lt;/span&gt;(self, user_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# update a single user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那么我们如何将其与路由系统联系起来呢？通过添加两个规则并明确提到每个规则的方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;user_view &lt;span style="color:#f92672"&gt;=&lt;/span&gt; UserAPI&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(&lt;span style="color:#e6db74"&gt;&amp;#39;user_api&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;, defaults&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;user_view, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;,])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;user_view, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;,])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;lt;int:user_id&amp;gt;&amp;#39;&lt;/span&gt;, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;user_view,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;DELETE&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你有很多类似的API，你可以重构那个注册码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;register_api&lt;/span&gt;(view, endpoint, url, pk&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, pk_type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;int&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; view_func &lt;span style="color:#f92672"&gt;=&lt;/span&gt; view&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_view(endpoint)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(url, defaults&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{pk: &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;view_func, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;,])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(url, view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;view_func, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;,])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; (url, pk_type, pk), view_func&lt;span style="color:#f92672"&gt;=&lt;/span&gt;view_func,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;DELETE&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;register_api(UserAPI, &lt;span style="color:#e6db74"&gt;&amp;#39;user_api&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;/users/&amp;#39;&lt;/span&gt;, pk&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 进阶 - 信号(Signals)</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-xin-hao-signals/</link><pubDate>Tue, 29 Jul 2025 10:56:01 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-xin-hao-signals/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 信号(Signals)" /&gt;&lt;p&gt;从Flask 0.6开始，Flask中集成了对信号的支持。这种支持由优秀的 &lt;a class="link" href="https://pypi.org/project/blinker/" target="_blank" rel="noopener"
 &gt;blinker&lt;/a&gt; 库提供，如果不可用，将优雅地退回。&lt;/p&gt;
&lt;p&gt;什么是信号？信号通过在核心框架或其他Flask扩展中的其他位置发生操作时发送通知来帮助您解耦应用程序。简而言之，信号允许某些发送者通知订阅者发生了什么事。&lt;/p&gt;
&lt;p&gt;Flask附带了几个信号，其他扩展可能提供更多信号。另请注意，信号旨在通知订阅者，不应鼓励订阅者修改数据。您会注意到有些信号看起来像一些内置装饰器那样做（例如： &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_started" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request_started&lt;/strong&gt;&lt;/a&gt; 与 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt; 非常相似）。但是，它们的工作方式存在差异。例如，核心 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt; 处理程序以特定顺序执行，并且能够通过返回响应来提前中止请求。相反，所有信号处理程序都以未定义的顺序执行，不会修改任何数据。&lt;/p&gt;
&lt;p&gt;信号优于处理程序的一大优势是，您可以一瞬间安全地订阅它们。例如，这些临时订阅有助于单元测试。假设您想知道哪些模板作为请求的一部分呈现：信号允许您完全执行此操作。&lt;/p&gt;
&lt;h3 id="订阅信号"&gt;订阅信号
&lt;/h3&gt;&lt;p&gt;要订阅信号，您可以使用信号的 &lt;a class="link" href="https://pythonhosted.org/blinker/index.html#blinker.base.Signal.connect" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;connect()&lt;/strong&gt;&lt;/a&gt; 方法。第一个参数是在发出信号时应该调用的函数，可选的第二个参数指定发送者。要取消订阅信号，可以使用 &lt;a class="link" href="https://pythonhosted.org/blinker/index.html#blinker.base.Signal.disconnect" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;disconnect()&lt;/strong&gt;&lt;/a&gt; 方法。&lt;/p&gt;
&lt;p&gt;对于所有核心Flask信号，发送方是发出信号的应用程序。订阅信号时，除非您真的想要收听来自所有应用程序的信号，否则请务必提供发件人。如果您正在开发扩展，则尤其如此。&lt;/p&gt;
&lt;p&gt;例如，这里有一个帮助器上下文管理器，可以在单元测试中使用它来确定呈现了哪些模板以及将哪些变量传递给模板：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; template_rendered
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; contextlib &lt;span style="color:#f92672"&gt;import&lt;/span&gt; contextmanager
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@contextmanager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;captured_templates&lt;/span&gt;(app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; recorded &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;record&lt;/span&gt;(sender, template, context, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;extra):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; recorded&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append((template, context))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; template_rendered&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect(record, app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt; recorded
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; template_rendered&lt;span style="color:#f92672"&gt;.&lt;/span&gt;disconnect(record, app)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在可以轻松地与测试客户端配对：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; captured_templates(app) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; templates:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; len(templates) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; template, context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; templates[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; template&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;index.html&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; len(context[&lt;span style="color:#e6db74"&gt;&amp;#39;items&amp;#39;&lt;/span&gt;]) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确保订阅额外的&lt;code&gt;**extra&lt;/code&gt;参数，以便在Flask为信号引入新参数时，您的调用不会失败。&lt;/p&gt;
&lt;p&gt;现在，应用程序 &lt;em&gt;app&lt;/em&gt; 在&lt;code&gt;with&lt;/code&gt;块体中发出的代码中的所有模板呈现都将记录在templates变量中。每当呈现模板时，模板对象以及上下文都会附加到其上。&lt;/p&gt;
&lt;p&gt;此外，还有一个方便的辅助方法（ &lt;a class="link" href="https://pythonhosted.org/blinker/index.html#blinker.base.Signal.connected_to" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;connected_to()&lt;/strong&gt;&lt;/a&gt; ），它允许您使用上下文管理器临时为函数订阅函数。因为无法以这种方式指定上下文管理器的返回值，所以必须将列表作为参数传递：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; template_rendered
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;captured_templates&lt;/span&gt;(app, recorded, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;extra):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;record&lt;/span&gt;(sender, template, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; recorded&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append((template, context))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; template_rendered&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connected_to(record, app)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的例子如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;templates &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; captured_templates(app, templates, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;extra):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; template, context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; templates[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="创建信号"&gt;创建信号
&lt;/h3&gt;&lt;p&gt;如果要在自己的应用程序中使用信号，可以直接使用blinker库。最常见的用例是自定义 &lt;a class="link" href="https://pythonhosted.org/blinker/index.html#blinker.base.Namespace" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Namespace&lt;/strong&gt;&lt;/a&gt; 中的命名信号。这是大多数时候建议的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; blinker &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Namespace
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;my_signals &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Namespace()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在您可以创建这样的新信号：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;model_saved &lt;span style="color:#f92672"&gt;=&lt;/span&gt; my_signals&lt;span style="color:#f92672"&gt;.&lt;/span&gt;signal(&lt;span style="color:#e6db74"&gt;&amp;#39;model-saved&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此处信号的名称使其独一无二，并简化了调试。您可以使用name属性访问信号的名称。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;####To扩展开发者:&lt;/p&gt;
&lt;p&gt;如果您正在编写Flask扩展，并且希望对缺少的blinker安装进行优雅降级，则可以使用flask.signals.Namespace类来执行此操作。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="发送信号"&gt;发送信号
&lt;/h3&gt;&lt;p&gt;如果要发出信号，可以通过调用 &lt;a class="link" href="https://pythonhosted.org/blinker/index.html#blinker.base.Signal.send" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;send()&lt;/strong&gt;&lt;/a&gt; 方法来实现。它接受发送方作为第一个参数，并可选择接收一些转发给信号订阅者的关键字参数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Model&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;save&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; model_saved&lt;span style="color:#f92672"&gt;.&lt;/span&gt;send(self)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;尽量选择一个好的发送者。如果您有一个发出信号的类，请将self作为发送者传递。如果从随机函数发出信号，则可以将&lt;code&gt;current_app._get_current_object()&lt;/code&gt;作为发送方传递。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;h3 id="将代理作为发送者传递"&gt;将代理作为发送者传递
&lt;/h3&gt;&lt;p&gt;永远不要将 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 作为发送者传递给信号。请改用&lt;code&gt;current_app._get_current_object()&lt;/code&gt;。原因是 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;current_app&lt;/strong&gt;&lt;/a&gt; 是代理而不是真正的应用程序对象。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="信号和flask的请求上下文"&gt;信号和Flask的请求上下文
&lt;/h3&gt;&lt;p&gt;信号在接收信号时完全支持&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/reqcontext/#request-context" target="_blank" rel="noopener"
 &gt;请求上下文&lt;/a&gt;。在 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_started" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request_started&lt;/strong&gt;&lt;/a&gt; 和 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_finished" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request_finished&lt;/strong&gt;&lt;/a&gt; 之间始终可以使用上下文局部变量，因此您可以根据需要依赖于 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;flask.g&lt;/strong&gt;&lt;/a&gt; 和其他变量。请注意 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/signals/#signals-sending" target="_blank" rel="noopener"
 &gt;发送信号&lt;/a&gt; 和 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.request_tearing_down" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request_tearing_down&lt;/strong&gt;&lt;/a&gt; 信号中描述的限制。&lt;/p&gt;
&lt;h3 id="基于装饰器的信号订阅"&gt;基于装饰器的信号订阅
&lt;/h3&gt;&lt;p&gt;使用Blinker 1.1，您还可以使用新的 &lt;strong&gt;connect_via()&lt;/strong&gt; 装饰器轻松订阅信号：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; template_rendered
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@template_rendered.connect_via&lt;/span&gt;(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;when_template_rendered&lt;/span&gt;(sender, template, context, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;extra):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print &lt;span style="color:#e6db74"&gt;&amp;#39;Template &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; is rendered with &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; (template&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name, context)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="核心信号"&gt;核心信号
&lt;/h3&gt;&lt;p&gt;查看&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#core-signals-list" target="_blank" rel="noopener"
 &gt;信号&lt;/a&gt; 以获取所有内置信号的列表。&lt;/p&gt;</description></item><item><title>Flask 1.0 进阶 - 配置处理</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-pei-zhi-chu-li/</link><pubDate>Tue, 29 Jul 2025 10:55:58 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-pei-zhi-chu-li/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 配置处理" /&gt;&lt;p&gt;应用程序需要某种配置。您可能希望更改不同的设置，具体取决于应用程序环境，例如切换调试模式，设置密钥以及其他此类特定于环境的内容。&lt;/p&gt;
&lt;p&gt;Flask的设计方式通常要求在应用程序启动时配置可用。您可以对代码中的配置进行硬编码，对于许多小型应用程序而言，实际上并不是那么糟糕，但有更好的方法。&lt;/p&gt;
&lt;p&gt;无论您如何加载配置，都有一个可用的配置对象，它保存已加载的配置值： &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; 对象的 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.config" target="_blank" rel="noopener"
 &gt;config&lt;/a&gt; 属性。这是Flask本身放置某些配置值的地方，也是扩展可以放置其配置值的地方。但这也是您可以拥有自己的配置的地方。&lt;/p&gt;
&lt;h2 id="基础配置"&gt;基础配置
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.config" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;config&lt;/strong&gt;&lt;/a&gt; 实际上是字典的子类，可以像任何字典一样进行修改：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;TESTING&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;某些配置值也会转发到 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; 对象，以便您可以从那里读取和写入它们：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;testing &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要一次更新多个密钥，可以使用 &lt;a class="link" href="https://docs.python.org/3/library/stdtypes.html#dict.update" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;dict.update()&lt;/strong&gt;&lt;/a&gt; 方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TESTING&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SECRET_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;_5#y2L&amp;#34;F4Q8z&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\xec&lt;/span&gt;&lt;span style="color:#e6db74"&gt;]/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="环境和调试功能"&gt;环境和调试功能
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#ENV" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ENV&lt;/strong&gt;&lt;/a&gt; 和 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#DEBUG" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;DEBUG&lt;/strong&gt;&lt;/a&gt; 配置值是特殊的，因为如果在应用程序开始设置后更改，它们可能会表现不一致。为了可靠地设置环境和调试模式，Flask使用环境变量。&lt;/p&gt;
&lt;p&gt;该环境用于指示Flask，扩展和其他程序，如Sentry，Flask正在运行的上下文。它由 &lt;strong&gt;FLASK_ENV&lt;/strong&gt; 环境变量控制，默认为&lt;code&gt;production&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;将&lt;strong&gt;FLASK_ENV&lt;/strong&gt;设置为&lt;code&gt;development&lt;/code&gt;将启用调试模式。&lt;code&gt;flask run&lt;/code&gt;将在调试模式下默认使用交互式调试器和重新加载器。要与环境分开控制，请使用 &lt;strong&gt;FLASK_DEBUG&lt;/strong&gt; 标志。&lt;/p&gt;
&lt;p&gt;要将Flask切换到开发环境并启用调试模式，请设置 &lt;strong&gt;FLASK_ENV&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export FLASK_ENV&lt;span style="color:#f92672"&gt;=&lt;/span&gt;development
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;建议使用上述环境变量。虽然可以在配置或代码中设置 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#ENV" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ENV&lt;/strong&gt;&lt;/a&gt; 和 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#DEBUG" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;DEBUG&lt;/strong&gt;&lt;/a&gt; ，但强烈建议不要这样做。使用flask命令无法及早读取它们，并且某些系统或扩展可能已根据先前的值配置了自身。&lt;/p&gt;
&lt;h2 id="内置配置值"&gt;内置配置值
&lt;/h2&gt;&lt;p&gt;Flask在内部使用以下配置值，这里只是简单介绍下比较常用的几个，具体的详细的配置值可以去 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/" target="_blank" rel="noopener"
 &gt;官网查看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="env"&gt;ENV
&lt;/h3&gt;&lt;p&gt;应用程序运行的环境。 Flask和扩展可以启用基于环境的行为，例如启用调试模式。&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.env" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;env&lt;/strong&gt;&lt;/a&gt; 属性映射到此配置键。这是由FLASK_ENV环境变量设置的，如果在代码中设置，可能不会按预期运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在生产环境中部署时不要启用开发模式&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;默认值：&lt;code&gt;'production'&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="debug"&gt;DEBUG
&lt;/h3&gt;&lt;p&gt;是否启用调试模式。 当使用&lt;code&gt;flask run&lt;/code&gt;启动开发服务器时，将显示一个交互式调试器，用于未处理的异常，并在代码更改时重新加载服务器。&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.debug" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;debug&lt;/strong&gt;&lt;/a&gt; 属性映射到此配置键。当&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/config/#ENV" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ENV&lt;/strong&gt;&lt;/a&gt; 为&lt;code&gt;'development'&lt;/code&gt;并被FLASK_DEBUG环境变量覆盖时，将启用此功能。如果在代码中设置，它可能不会按预期运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在生产中部署时，请勿启用调试模式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;默认值：如果ENV为&lt;code&gt;'development'&lt;/code&gt;时值为&lt;code&gt;True&lt;/code&gt;，否则为&lt;code&gt;False&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="testing"&gt;TESTING
&lt;/h3&gt;&lt;p&gt;启用测试模式。 异常传播而不是由应用程序的错误处理程序处理。扩展也可能会改变其行为，以方便测试。您应该在自己的测试中启用它。&lt;/p&gt;
&lt;p&gt;默认值：&lt;code&gt;False&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="propagate_exceptions"&gt;PROPAGATE_EXCEPTIONS
&lt;/h3&gt;&lt;p&gt;异常被重新引发而不是由应用程序的错误处理程序处理。&lt;/p&gt;
&lt;p&gt;如果没有设置，在启用了TESTING或DEBUG，则隐含为true。&lt;/p&gt;
&lt;p&gt;默认值: &lt;code&gt;None&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="preserve_context_on_exception"&gt;PRESERVE_CONTEXT_ON_EXCEPTION
&lt;/h3&gt;&lt;p&gt;发生异常时不要弹出请求上下文。&lt;/p&gt;
&lt;p&gt;如果未设置，在DEBUG为true时，则为true。这允许调试器在错误上内省请求数据，通常不需要直接设置。&lt;/p&gt;
&lt;p&gt;默认值: &lt;code&gt;None&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="secret_key"&gt;SECRET_KEY
&lt;/h3&gt;&lt;p&gt;用于安全签名会话cookie的密钥，可用于扩展或您的应用程序的任何其他安全相关需求。它应该是一个长的随机字节串，尽管也接受unicode。例如，将其输出复制到您的配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -c &lt;span style="color:#e6db74"&gt;&amp;#39;import os; print(os.urandom(16))&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b&lt;span style="color:#e6db74"&gt;&amp;#39;_5#y2L&amp;#34;F4Q8z\n\xec]/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;提交问题或提交代码时不要泄露密钥。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;默认值: &lt;code&gt;None&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="从文件配置"&gt;从文件配置
&lt;/h2&gt;&lt;p&gt;如果您可以将其存储在单独的文件中，理想情况下位于实际应用程序包之外，则配置会变得更有用。这使得可以通过各种包处理工具（&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/patterns/distribute/#distribute-deployment" target="_blank" rel="noopener"
 &gt;使用Setuptools进行部署&lt;/a&gt;）来打包和分发应用程序，最后修改配置文件。&lt;/p&gt;
&lt;p&gt;所以一个常见的模式是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(&lt;span style="color:#e6db74"&gt;&amp;#39;yourapplication.default_settings&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_envvar(&lt;span style="color:#e6db74"&gt;&amp;#39;YOURAPPLICATION_SETTINGS&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;首先从 &lt;em&gt;yourapplication.default_settings&lt;/em&gt; 模块加载配置，然后使用&lt;strong&gt;YOURAPPLICATION_SETTINGS&lt;/strong&gt;环境变量指向的文件内容覆盖值。在启动服务器之前，可以使用shell中的export命令在Linux或OS X上设置此环境变量：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export YOURAPPLICATION_SETTINGS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/path/to/settings.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python run-app.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Running on http://127.0.0.1:5000/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Restarting with reloader...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Windows系统上使用内置的 &lt;em&gt;set&lt;/em&gt; 代替：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;set YOURAPPLICATION_SETTINGS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;ath&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;o&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;ettings.cfg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置文件本身就是实际的Python文件。以后只有大写的值实际存储在配置对象中。因此，请确保为配置键使用大写字母。&lt;/p&gt;
&lt;p&gt;以下是配置文件的示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Example configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DEBUG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SECRET_KEY &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;_5#y2L&amp;#34;F4Q8z&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\xec&lt;/span&gt;&lt;span style="color:#e6db74"&gt;]/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确保尽早加载配置，以便扩展能够在启动时访问配置。配置对象上还有其他方法可以从单个文件加载。有关完整参考，请阅读 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Config" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Config&lt;/strong&gt;&lt;/a&gt; 对象的文档。&lt;/p&gt;
&lt;h2 id="从环境变量配置"&gt;从环境变量配置
&lt;/h2&gt;&lt;p&gt;除了使用环境变量指向配置文件之外，您可能会发现直接从环境控制配置值很有用（或必要）。&lt;/p&gt;
&lt;p&gt;在启动服务器之前，可以使用shell中的export命令在Linux或OS X上设置环境变量：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export SECRET_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;5f352379324c22463451387a0aec5d2f&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export MAIL_ENABLED&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python run-app.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Running on http://127.0.0.1:5000/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Windows系统上使用内置的&lt;code&gt;set&lt;/code&gt;代替：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;set SECRET_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;5f352379324c22463451387a0aec5d2f&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然这种方法很容易使用，但重要的是要记住环境变量是字符串 - 它们不会自动反序列化为Python类型。&lt;/p&gt;
&lt;p&gt;以下是使用环境变量的配置文件的示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_mail_enabled &lt;span style="color:#f92672"&gt;=&lt;/span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;environ&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;MAIL_ENABLED&amp;#34;&lt;/span&gt;, default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_ENABLED &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _mail_enabled&lt;span style="color:#f92672"&gt;.&lt;/span&gt;lower() &lt;span style="color:#f92672"&gt;in&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;t&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SECRET_KEY &lt;span style="color:#f92672"&gt;=&lt;/span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;environ&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;SECRET_KEY&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; SECRET_KEY:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;No SECRET_KEY set for Flask application&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，除了空字符串之外的任何值都将被解释为Python中的布尔值&lt;code&gt;True&lt;/code&gt;，如果环境显式设置了值为&lt;code&gt;False&lt;/code&gt;的值，则需要注意。&lt;/p&gt;
&lt;p&gt;确保尽早加载配置，以便扩展能够在启动时访问配置。配置对象上还有其他方法可以从单个文件加载。有关完整参考，请阅读 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Config" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Config&lt;/strong&gt;&lt;/a&gt; 类文档。&lt;/p&gt;
&lt;h2 id="配置最佳实践"&gt;配置最佳实践
&lt;/h2&gt;&lt;p&gt;前面提到的方法的缺点是它使测试更加困难。一般来说，这个问题没有单一的100％解决方案，但是您可以记住以下几点来改善这种体验：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在函数中创建应用程序并在其上注册blueprint。这样，您可以创建应用程序的多个实例，并附加不同的配置，这使得单元测试变得更加容易。您可以根据需要使用它来传递配置。&lt;/li&gt;
&lt;li&gt;不要在导入时编写需要配置的代码。如果您将自己限制为仅对请求进行配置访问，则可以根据需要稍后重新配置对象。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="开发生产"&gt;开发/生产
&lt;/h2&gt;&lt;p&gt;大多数应用程序需要多个配置。生产服务器和开发期间使用的配置至少应该是单独的配置。处理此问题的最简单方法是使用始终加载的默认配置和版本控制的一部分，以及根据上述示例中提到的必要覆盖值的单独配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(&lt;span style="color:#e6db74"&gt;&amp;#39;yourapplication.default_settings&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_envvar(&lt;span style="color:#e6db74"&gt;&amp;#39;YOURAPPLICATION_SETTINGS&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后你只需要添加一个单独的config.py文件并导出YOURAPPLICATION_SETTINGS=/path/to/config.py就完成了。然而，也有其他方法。例如，您可以使用导入或子类化。&lt;/p&gt;
&lt;p&gt;Django世界中非常流行的是通过将&lt;code&gt;yourapplication.default_settings import *&lt;/code&gt;添加到文件顶部然后手动覆盖更改，在配置文件中显式导入。您还可以检查环境变量，如YOURAPPLICATION_MODE，并将其设置为生产，开发等，并根据该变量导入不同的硬编码文件。&lt;/p&gt;
&lt;p&gt;一个有趣的模式也是使用类和继承进行配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Config&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DEBUG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TESTING &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DATABASE_URI &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;sqlite:///:memory:&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ProductionConfig&lt;/span&gt;(Config):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DATABASE_URI &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;mysql://user@localhost/foo&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DevelopmentConfig&lt;/span&gt;(Config):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DEBUG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestingConfig&lt;/span&gt;(Config):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TESTING &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要启用此类配置，您只需调用from_object()：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(&lt;span style="color:#e6db74"&gt;&amp;#39;configmodule.ProductionConfig&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，from_object()不会实例化类对象。如果需要实例化类，例如访问属性，则必须在调用from_object()之前执行此操作：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; configmodule &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ProductionConfig
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(ProductionConfig())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Alternatively, import via string:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.utils &lt;span style="color:#f92672"&gt;import&lt;/span&gt; import_string
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cfg &lt;span style="color:#f92672"&gt;=&lt;/span&gt; import_string(&lt;span style="color:#e6db74"&gt;&amp;#39;configmodule.ProductionConfig&amp;#39;&lt;/span&gt;)()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(cfg)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实例化配置对象允许您在配置类中使用&lt;code&gt;@property&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Config&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Base config, uses staging database server.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DEBUG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TESTING &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DB_SERVER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;192.168.1.56&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DATABASE_URI&lt;/span&gt;(self): &lt;span style="color:#75715e"&gt;# Note: all caps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;mysql://user@&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/foo&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DB_SERVER)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ProductionConfig&lt;/span&gt;(Config):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Uses production database server.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DB_SERVER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;192.168.19.32&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DevelopmentConfig&lt;/span&gt;(Config):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DB_SERVER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DEBUG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestingConfig&lt;/span&gt;(Config):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DB_SERVER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DEBUG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DATABASE_URI &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;sqlite:///:memory:&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有许多不同的方法，由您决定如何管理配置文件。不过这里列出了很好的建议：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在版本控制中保留默认配置。使用此默认配置填充配置，或者在覆盖值之前将其导入您自己的配置文件中。&lt;/li&gt;
&lt;li&gt;使用环境变量在配置之间切换。这可以从Python解释器外部完成，并使开发和部署变得更加容易，因为您可以快速轻松地在不同的配置之间切换而无需触摸代码。如果您经常在不同的项目上工作，您甚至可以创建自己的源代码脚本来激活virtualenv并为您导出开发配置。&lt;/li&gt;
&lt;li&gt;在生产中使用类似&lt;a class="link" href="http://www.fabfile.org/" target="_blank" rel="noopener"
 &gt;fabric&lt;/a&gt;的工具将代码和配置分别推送到生产服务器。有关如何执行此操作的一些详细信息，请转到&lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/patterns/fabric/#fabric-deployment" target="_blank" rel="noopener"
 &gt;使用Fabric进行部署模式&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="实例文件夹"&gt;实例文件夹
&lt;/h2&gt;&lt;p&gt;Flask 0.8引入了实例文件夹。Flask很长一段时间可以直接引用相对于应用程序文件夹的路径（通过 &lt;strong&gt;Flask.root_path&lt;/strong&gt; ）。这也是有很多开发人员加载了存储在应用程序旁边的配置。不幸的是，这只适用于应用程序不是包的情况，在这种情况下根路径引用包的内容。&lt;/p&gt;
&lt;p&gt;随着Flask 0.8引入了一个新属性： &lt;strong&gt;Flask.instance_path&lt;/strong&gt; 。它指的是一个名为“实例文件夹”的新概念。实例文件夹旨在不受版本控制，并且是特定于部署的。它是丢弃在运行时或配置文件中更改的东西的理想场所。&lt;/p&gt;
&lt;p&gt;您可以在创建Flask应用程序时显式提供实例文件夹的路径，也可以让Flask自动检测实例文件夹。对于显式配置，请使用 &lt;em&gt;instance_path&lt;/em&gt; 参数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__, instance_path&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;/path/to/instance/folder&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请记住，提供时此路径必须是绝对的。&lt;/p&gt;
&lt;p&gt;如果未提供 &lt;em&gt;instance_path&lt;/em&gt; 参数，则使用以下默认位置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;未安装模块：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/myapp.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/instance
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;未安装包：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/myapp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /__init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/instance
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;已安装的模块或包装：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$PREFIX/lib/python2.X/site-packages/myapp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$PREFIX/var/myapp-instance
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;$PREFIX&lt;/code&gt;是Python安装的前缀。这可以是&lt;code&gt;/usr&lt;/code&gt;或virtualenv的路径。您可以打印&lt;code&gt;sys.prefix&lt;/code&gt;的值以查看前缀的设置。&lt;/p&gt;
&lt;p&gt;由于配置对象提供了从相对文件名加载配置文件，因此我们可以通过文件名将加载更改为相对于实例路径（如果需要）。配置文件中相对路径的行为可以在“相对于应用程序根目录”（默认）到“相对于实例文件夹”之间通过 &lt;em&gt;instance_relative_config&lt;/em&gt; 切换到应用程序构造函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__, instance_relative_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是一个完整的示例，说明如何配置Flask以从模块预加载配置，然后从实例文件夹中的文件覆盖配置（如果存在）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__, instance_relative_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(&lt;span style="color:#e6db74"&gt;&amp;#39;yourapplication.default_settings&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_pyfile(&lt;span style="color:#e6db74"&gt;&amp;#39;application.cfg&amp;#39;&lt;/span&gt;, silent&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以通过 &lt;strong&gt;Flask.instance_path&lt;/strong&gt; 找到实例文件夹的路径。Flask还提供了使用 &lt;strong&gt;Flask.open_instance_resource()&lt;/strong&gt; 从实例文件夹中打开文件的快捷方式。&lt;/p&gt;
&lt;p&gt;两者的示例用法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;filename &lt;span style="color:#f92672"&gt;=&lt;/span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;instance_path, &lt;span style="color:#e6db74"&gt;&amp;#39;application.cfg&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; open(filename) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# or via open_instance_resource:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;open_instance_resource(&lt;span style="color:#e6db74"&gt;&amp;#39;application.cfg&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 进阶 - 日志</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ri-zhi/</link><pubDate>Tue, 29 Jul 2025 10:55:51 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ri-zhi/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 日志" /&gt;&lt;p&gt;Flask使用标准的Python日志记录。所有与Flask相关的消息都记录在&amp;rsquo;flask&amp;rsquo;记录器命名空间下。Flask.logger返回名为“flask.app”的记录器，可用于记录应用程序的消息。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; current_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM user WHERE username = ?&amp;#39;&lt;/span&gt;, (username,)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; user &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect username.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; logged fail&amp;#39;&lt;/span&gt;, username)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;elif&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; check_password_hash(user[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;], password):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; logged fail&amp;#39;&lt;/span&gt;, username)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect password.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; error &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;clear()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session[&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user[&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; logged successfully&amp;#39;&lt;/span&gt;, username)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;blog.index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flash(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;auth/login.html&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="基础配置"&gt;基础配置
&lt;/h2&gt;&lt;p&gt;如果要为项目配置日志记录，则应在程序启动时尽快执行。如果在配置日志记录之前访问 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.logger" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.logger&lt;/strong&gt;&lt;/a&gt; ，它将添加默认处理程序。如果可能，在创建应用程序对象之前配置日志记录&lt;/p&gt;
&lt;p&gt;此示例使用 &lt;a class="link" href="https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;dictConfig()&lt;/strong&gt;&lt;/a&gt; 创建类似于Flask默认的日志记录配置，但所有日志除外：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; logging.config &lt;span style="color:#f92672"&gt;import&lt;/span&gt; dictConfig
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dictConfig({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;version&amp;#39;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;formatters&amp;#39;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;format&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(asctime)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;] &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(levelname)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;in &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(module)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(message)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;handlers&amp;#39;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;wsgi&amp;#39;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;logging.StreamHandler&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;stream&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;ext://flask.logging.wsgi_errors_stream&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;formatter&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;level&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;INFO&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;handlers&amp;#39;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#39;wsgi&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="默认配置"&gt;默认配置
&lt;/h2&gt;&lt;p&gt;如果您没有自己配置日志记录，Flask会自动将一个 &lt;a class="link" href="https://docs.python.org/3/library/logging.handlers.html#logging.StreamHandler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;StreamHandler&lt;/strong&gt;&lt;/a&gt; 添加到 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.logger" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.logger&lt;/strong&gt;&lt;/a&gt; 。在请求期间，它将在&lt;code&gt;environ ['wsgi.errors']&lt;/code&gt;（通常是 &lt;a class="link" href="https://docs.python.org/3/library/sys.html#sys.stderr" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;sys.stderr&lt;/strong&gt;&lt;/a&gt; ）中写入由WSGI服务器指定的流。在请求之外，它将记录到 &lt;a class="link" href="https://docs.python.org/3/library/sys.html#sys.stderr" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;sys.stderr&lt;/strong&gt;&lt;/a&gt; 。&lt;/p&gt;
&lt;h2 id="移除默认的handler"&gt;移除默认的Handler
&lt;/h2&gt;&lt;p&gt;如果您在访问 &lt;a class="link" href="https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.logger" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.logger&lt;/strong&gt;&lt;/a&gt; 后配置了日志记录，并且需要删除默认处理程序，则可以导入和删除它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.logging &lt;span style="color:#f92672"&gt;import&lt;/span&gt; default_handler
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;removeHandler(default_handler)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="向管理员发送电子邮件错误"&gt;向管理员发送电子邮件错误
&lt;/h2&gt;&lt;p&gt;在远程服务器上运行应用程序进行生产时，您可能不会经常查看日志消息。WSGI服务器可能会将日志消息发送到文件，如果用户告诉您出错，只要检查该文件就可以了。&lt;/p&gt;
&lt;p&gt;要主动发现和修复错误，您可以配置 &lt;a class="link" href="https://docs.python.org/3/library/logging.handlers.html#logging.handlers.SMTPHandler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;logging.handlers.SMTPHandler&lt;/strong&gt;&lt;/a&gt; 以在记录错误和更高错误时发送电子邮件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; logging.handlers &lt;span style="color:#f92672"&gt;import&lt;/span&gt; SMTPHandler
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mail_handler &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SMTPHandler(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;server&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;server_port&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromaddr&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;[email protected]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toaddrs&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;[email protected]&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subject&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Application Error&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mail_handler&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setLevel(logging&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ERROR)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mail_handler&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setFormatter(logging&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Formatter(&lt;span style="color:#e6db74"&gt;&amp;#39;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(asctime)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;] &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(levelname)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;in &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(module)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(message)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;addHandler(mail_handler)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这要求您在同一服务器上设置SMTP服务器。有关配置处理程序的更多信息，请参阅Python文档。&lt;/p&gt;
&lt;h2 id="注入请求信息"&gt;注入请求信息
&lt;/h2&gt;&lt;p&gt;查看有关请求的更多信息（例如IP地址）可能有助于调试某些错误。您可以将 &lt;a class="link" href="https://docs.python.org/3/library/logging.html#logging.Formatter" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;logging.Formatter&lt;/strong&gt;&lt;/a&gt; 子类化为注入可以在消息中使用的自己的字段。您可以更改Flask的默认处理程序，上面定义的邮件处理程序或任何其他处理程序的格式化程序。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.logging &lt;span style="color:#f92672"&gt;import&lt;/span&gt; default_handler
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_request_context,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;logging_common_formatter&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RequestFormatter&lt;/span&gt;(logging&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Formatter):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;format&lt;/span&gt;(self, record):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; has_request_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; record&lt;span style="color:#f92672"&gt;.&lt;/span&gt;url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;url
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; record&lt;span style="color:#f92672"&gt;.&lt;/span&gt;remote_addr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;remote_addr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; record&lt;span style="color:#f92672"&gt;.&lt;/span&gt;url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; record&lt;span style="color:#f92672"&gt;.&lt;/span&gt;remote_addr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; super()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(record)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; formatter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; RequestFormatter(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(asctime)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;] &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(remote_addr)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; requested &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(url)s&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(levelname)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; in &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(module)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(message)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; formatter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;formatter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; logging_common_formatter()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mail_handler&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setFormatter(formatter)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;default_handler&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setFormatter(formatter)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="其他库"&gt;其他库
&lt;/h2&gt;&lt;p&gt;其他库可能会广泛使用日志记录，您也希望从这些日志中查看相关消息。最简单的方法是将处理程序添加到root记录器而不是仅添加应用程序记录器。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.logging &lt;span style="color:#f92672"&gt;import&lt;/span&gt; default_handler
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root &lt;span style="color:#f92672"&gt;=&lt;/span&gt; logging&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getLogger()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root&lt;span style="color:#f92672"&gt;.&lt;/span&gt;addHandler(default_handler)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root&lt;span style="color:#f92672"&gt;.&lt;/span&gt;addHandler(mail_handler)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据您的项目，配置您关注的每个记录器可能更有用，而不是仅配置root记录器。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; logger &lt;span style="color:#f92672"&gt;in&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logging&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getLogger(&lt;span style="color:#e6db74"&gt;&amp;#39;sqlalchemy&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logging&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getLogger(&lt;span style="color:#e6db74"&gt;&amp;#39;other_package&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;addHandler(default_handler)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;addHandler(mail_handler)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="werkzeug"&gt;Werkzeug
&lt;/h2&gt;&lt;p&gt;Werkzeug将基本请求/响应信息记录到&lt;code&gt;'werkzeug'&lt;/code&gt;记录器。如果root记录器没有配置处理程序，Werkzeug会将 &lt;a class="link" href="https://docs.python.org/3/library/logging.handlers.html#logging.StreamHandler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;StreamHandler&lt;/strong&gt;&lt;/a&gt; 添加到其记录器中。&lt;/p&gt;
&lt;h2 id="flask扩展"&gt;Flask扩展
&lt;/h2&gt;&lt;p&gt;根据具体情况，扩展程序可以选择记录到app.logger或其自己的命名记录程序。有关详细信息，请参阅每个扩展的文档。&lt;/p&gt;</description></item><item><title>Flask 1.0 进阶 - 应用程序错误</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ying-yong-cheng-xu-cuo-wu/</link><pubDate>Tue, 29 Jul 2025 10:55:48 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ying-yong-cheng-xu-cuo-wu/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 应用程序错误" /&gt;&lt;p&gt;应用程序失败，服务器失败迟早你会看到生产中的例外情况。即使您的代码100％正确，您仍会不时看到异常。为什么？因为所涉及的一切都会失败。以下是一些完美的代码可能导致服务器错误的情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客户端提前终止了请求，应用程序仍然从传入的数据中读取&lt;/li&gt;
&lt;li&gt;数据库服务器挂了，无法处理查询&lt;/li&gt;
&lt;li&gt;文件系统已满&lt;/li&gt;
&lt;li&gt;一个硬盘崩溃了&lt;/li&gt;
&lt;li&gt;一个后端服务器挂了&lt;/li&gt;
&lt;li&gt;正在使用的库中出现了编程错误&lt;/li&gt;
&lt;li&gt;服务器与另一个系统的网络连接失败&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这只是您可能面临的一小部分问题。那么我们如何处理这类问题呢？默认情况下，如果您的应用程序在生产模式下运行，Flask将为您显示一个非常简单的页面，并将异常记录到 &lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.logger" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;logger&lt;/strong&gt;&lt;/a&gt; 中。&lt;/p&gt;
&lt;p&gt;但是你可以做的更多，我们将介绍一些更好的设置来处理错误。&lt;/p&gt;
&lt;h2 id="错误记录工具"&gt;错误记录工具
&lt;/h2&gt;&lt;p&gt;如果有足够多的用户遇到错误并且通常从不查看日志文件，那么发送错误邮件（即使仅针对关键邮件）也会变得无法控制。这就是我们建议使用&lt;a class="link" href="https://www.getsentry.com/" target="_blank" rel="noopener"
 &gt;Sentry&lt;/a&gt;处理应用程序错误的原因。它可以在&lt;a class="link" href="https://github.com/getsentry/sentry" target="_blank" rel="noopener"
 &gt;GitHub&lt;/a&gt;上作为开源项目使用，也可以作为托管版本使用，您可以免费试用。Sentry聚合重复错误，捕获完整堆栈跟踪和本地变量以进行调试，并根据新错误或频率阈值向您发送邮件。&lt;/p&gt;
&lt;p&gt;要使用Sentry，您需要安装具有额外Flask依赖性的raven客户端：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install raven&lt;span style="color:#f92672"&gt;[&lt;/span&gt;flask&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后将其添加到Flask应用中：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; raven.contrib.flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Sentry
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sentry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Sentry(app, dsn&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;YOUR_DSN_HERE&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者，如果您正在使用工厂，您也可以稍后启动它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; raven.contrib.flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Sentry
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sentry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Sentry(dsn&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;YOUR_DSN_HERE&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sentry&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要使用从Sentry安装获得的DSN值替换YOUR_DSN_HERE值。&lt;/p&gt;
&lt;p&gt;之后故障会自动报告给Sentry，您可以从那里收到错误通知。&lt;/p&gt;
&lt;h2 id="错误处理程序"&gt;错误处理程序
&lt;/h2&gt;&lt;p&gt;您可能希望在发生错误时向用户显示自定义错误页面。这可以通过注册错误处理程序来完成。&lt;/p&gt;
&lt;p&gt;错误处理程序是返回响应的普通视图函数，但它不是为路由注册，而是注册在尝试处理请求时引发的异常或HTTP状态代码。&lt;/p&gt;
&lt;h3 id="注册"&gt;注册
&lt;/h3&gt;&lt;p&gt;通过使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.errorhandler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;errorhandler()&lt;/strong&gt;&lt;/a&gt; 装饰函数来注册处理程序。或者使用 &lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.register_error_handler" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;register_error_handler()&lt;/strong&gt;&lt;/a&gt; 稍后注册该函数。请记住在返回响应时设置错误代码。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.errorhandler&lt;/span&gt;(werkzeug&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exceptions&lt;span style="color:#f92672"&gt;.&lt;/span&gt;BadRequest)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;handle_bad_request&lt;/span&gt;(e):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;bad request!&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# or, without the decorator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register_error_handler(&lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, handle_bad_request)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.HTTPException" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;werkzeug.exceptions.HTTPException&lt;/strong&gt;&lt;/a&gt; 子类如 &lt;a class="link" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.BadRequest" target="_blank" rel="noopener"
 &gt;BadRequest&lt;/a&gt; 及其HTTP代码在注册处理程序时是可互换的。（&lt;code&gt;BadRequest.code == 400&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;代码无法注册非标准HTTP代码，因为Werkzeug不知道它们。相反，使用适当的代码定义 &lt;a class="link" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.HTTPException" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;HTTPException&lt;/strong&gt;&lt;/a&gt; 的子类并注册并引发该异常类。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;InsufficientStorage&lt;/span&gt;(werkzeug&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exceptions&lt;span style="color:#f92672"&gt;.&lt;/span&gt;HTTPException):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;507&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Not enough storage space.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register_error_handler(InsuffcientStorage, handle_507)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; InsufficientStorage()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以为任何异常类注册处理程序，而不仅仅是HTTPException子类或HTTP状态代码。可以为特定类或父类的所有子类注册处理程序。&lt;/p&gt;
&lt;h3 id="处理"&gt;处理
&lt;/h3&gt;&lt;p&gt;当Flask在处理请求时捕获到异常时，首先按代码查找。如果没有为代码注册处理程序，则按类层次结构查找;选择最具体的处理程序。如果未注册任何处理程序，则&lt;a class="link" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.HTTPException" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;HTTPException&lt;/strong&gt;&lt;/a&gt; 子类显示有关其代码的通用消息，而其他异常则转换为通用500内部服务器错误。&lt;/p&gt;
&lt;p&gt;例如，如果引发了 &lt;a class="link" href="https://docs.python.org/3/library/exceptions.html#ConnectionRefusedError" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ConnectionRefusedError&lt;/strong&gt;&lt;/a&gt; 的实例，并且为 &lt;a class="link" href="https://docs.python.org/3/library/exceptions.html#ConnectionError" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ConnectionError&lt;/strong&gt;&lt;/a&gt; 和 &lt;a class="link" href="https://docs.python.org/3/library/exceptions.html#ConnectionRefusedError" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ConnectionRefusedError&lt;/strong&gt;&lt;/a&gt; 注册了处理程序，则使用异常实例调用更具体的 &lt;a class="link" href="https://docs.python.org/3/library/exceptions.html#ConnectionRefusedError" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;ConnectionRefusedError&lt;/strong&gt;&lt;/a&gt; 处理程序以生成响应。&lt;/p&gt;
&lt;p&gt;在blueprint上注册的处理程序优先于在应用程序上全局注册的处理程序，假设blueprint正在处理引发异常的请求。但是，blueprint无法处理404路由错误，因为404可以在确定blueprint之前发生在路由级别。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;版本0.11： 处理程序的优先级取决于它们注册的异常类的特殊性，而不是它们注册的顺序&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="日志记录"&gt;日志记录
&lt;/h3&gt;&lt;p&gt;有关如何记录异常的信息，请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/logging/#logging" target="_blank" rel="noopener"
 &gt;记录&lt;/a&gt;，例如通过电子邮件将其发送给管理员。&lt;/p&gt;
&lt;h2 id="调试应用程序错误"&gt;调试应用程序错误
&lt;/h2&gt;&lt;p&gt;对于生产应用程序，使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/errorhandling/#application-errors" target="_blank" rel="noopener"
 &gt;应用程序错误&lt;/a&gt;中所述的日志记录和通知配置应用程序。本节提供了调试部署配置和深入挖掘全功能Python调试器的指针。&lt;/p&gt;
&lt;h2 id="当有疑问时手动运行"&gt;当有疑问时，手动运行
&lt;/h2&gt;&lt;p&gt;在为生产配置应用程序时遇到问题？如果您具有对主机的shell访问权限，请验证您是否可以从部署环境中的shell手动运行应用程序。请确保在与配置的部署相同的用户帐户下运行，以解决权限问题。您可以在生产主机上使用带有 &lt;em&gt;debug = True&lt;/em&gt; 的Flask内置开发服务器，这有助于捕获配置问题，但 &lt;strong&gt;请务必在受控环境中暂时执行此操作&lt;/strong&gt; 。不要使用 &lt;em&gt;debug = True&lt;/em&gt; 在生产中运行。&lt;/p&gt;
&lt;h2 id="使用调试器"&gt;使用调试器
&lt;/h2&gt;&lt;p&gt;为了深入挖掘，可能需要跟踪代码执行，Flask提供了一个开箱即用的调试器（参见&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode" target="_blank" rel="noopener"
 &gt;调试模式&lt;/a&gt;）。如果您想使用其他Python调试器，请注意调试器会相互干扰。您必须设置一些选项才能使用您喜欢的调试器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;debug&lt;/code&gt; - 是否启用调试模式和捕获异常&lt;/li&gt;
&lt;li&gt;&lt;code&gt;use_debugger&lt;/code&gt; - 是否使用内部Flask调试器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;use_reloader&lt;/code&gt; - 是否在异常时重新加载和分叉进程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;debug&lt;/code&gt;必须为True（即必须捕获异常）才能使其他两个选项具有任何值。&lt;/p&gt;
&lt;p&gt;如果您使用Aptana/Eclipse进行调试，则需要将&lt;code&gt;use_debugger&lt;/code&gt;和&lt;code&gt;use_reloader&lt;/code&gt;都设置为False。&lt;/p&gt;
&lt;p&gt;一个可能有用的配置模式是在config.yaml中设置以下内容（当然，根据您的应用程序更改块）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;FLASK&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;DEBUG&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;DEBUG_WITH_APTANA&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在你的应用程序的入口点（main.py），你可以有类似的东西：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# To allow aptana to receive errors, set use_debugger=False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_app(config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;config.yaml&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug: use_debugger &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Disable Flask&amp;#39;s debugger if external debugger is requested&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; use_debugger &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt;(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;DEBUG_WITH_APTANA&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run(use_debugger&lt;span style="color:#f92672"&gt;=&lt;/span&gt;use_debugger, debug&lt;span style="color:#f92672"&gt;=&lt;/span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; use_reloader&lt;span style="color:#f92672"&gt;=&lt;/span&gt;use_debugger, host&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;0.0.0.0&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 进阶 - 测试Flask应用程序</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ce-shi-flask-ying-yong-cheng-xu/</link><pubDate>Tue, 29 Jul 2025 10:55:45 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-ce-shi-flask-ying-yong-cheng-xu/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 测试Flask应用程序" /&gt;&lt;p&gt;&amp;ldquo;未经测试的东西被打破了&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这句话的起源是未知的，虽然它并不完全正确，但它也离真相不远。未经测试的应用程序使得现有代码难以改进，未经测试的应用程序的开发人员往往变得非常偏执。如果应用程序具有自动化测试，您可以安全地进行更改，并立即知道是否有任何中断。&lt;/p&gt;
&lt;p&gt;Flask提供了一种方法来测试您的应用程序，方法是暴露Werkzeug测试&lt;a class="link" href="http://werkzeug.pocoo.org/docs/test/#werkzeug.test.Client" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Client&lt;/strong&gt;&lt;/a&gt;并为您处理上下文本地。然后，您可以将其与您喜欢的测试解决方案一起使用。&lt;/p&gt;
&lt;p&gt;在本分享中，我们将使用&lt;a class="link" href="https://pytest.org/" target="_blank" rel="noopener"
 &gt;pytest&lt;/a&gt;包作为测试的基础框架。您可以使用pip安装它，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install pytest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="应用程序"&gt;应用程序
&lt;/h3&gt;&lt;p&gt;首先，我们需要一个应用程序来测试;我们将使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/tutorial/#tutorial" target="_blank" rel="noopener"
 &gt;教程&lt;/a&gt;中的应用程序。如果您还没有该应用程序，请从&lt;a class="link" href="https://github.com/durban89/flask-baby" target="_blank" rel="noopener"
 &gt;示例中&lt;/a&gt;获取源代码。&lt;/p&gt;
&lt;h3 id="测试骨架搭建"&gt;测试骨架搭建
&lt;/h3&gt;&lt;p&gt;我们首先在应用程序根目录下添加一个tests目录(如果有的话可以不用创建)。然后创建一个Python文件来存储我们的测试（&lt;code&gt;test_baby.py&lt;/code&gt;）。当我们格式化文件名如&lt;code&gt;test_*.py&lt;/code&gt;时，它将被pytest自动发现。&lt;/p&gt;
&lt;p&gt;接下来，我们创建一个名为 &lt;strong&gt;client()&lt;/strong&gt; 的&lt;a class="link" href="https://docs.pytest.org/en/latest/fixture.html" target="_blank" rel="noopener"
 &gt;pytest fixture&lt;/a&gt;，它配置应用程序以进行测试并初始化一个新的数据库，代码如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; tempfile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; init_db, get_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; open(os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dirname(__file__), &lt;span style="color:#e6db74"&gt;&amp;#39;data.sql&amp;#39;&lt;/span&gt;), &lt;span style="color:#e6db74"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _data_sql &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;decode(&lt;span style="color:#e6db74"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db_fd, db_path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tempfile&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkstemp()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_app({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;DATABASE&amp;#39;&lt;/span&gt;: db_path,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;TESTING&amp;#39;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client &lt;span style="color:#f92672"&gt;=&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; init_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; get_db()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;executescript(_data_sql)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt; client
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close(db_fd)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;DATABASE&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个单独的测试将调用此client fixture。它为我们提供了一个简单的应用程序接口，我们可以在其中触发对应用程序的测试请求。client还将为我们跟踪cookie。&lt;/p&gt;
&lt;p&gt;在设置过程中，TESTING配置标志被激活。这样做是在请求处理期间禁用错误捕获，以便在对应用程序执行测试请求时获得更好的错误报告。&lt;/p&gt;
&lt;p&gt;因为SQLite3是基于文件系统的，所以我们可以轻松地使用&lt;a class="link" href="https://docs.python.org/3/library/tempfile.html#module-tempfile" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;tempfile&lt;/strong&gt;&lt;/a&gt;模块来创建临时数据库并对其进行初始化。&lt;a class="link" href="https://docs.python.org/3/library/tempfile.html#tempfile.mkstemp" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;mkstemp()&lt;/strong&gt;&lt;/a&gt;函数为我们做了两件事：它返回一个低级文件句柄和一个随机文件名，后者我们用作数据库名。我们必须保持db_fd，以便我们可以使用 &lt;strong&gt;os.close()&lt;/strong&gt; 函数来关闭文件。&lt;/p&gt;
&lt;p&gt;要在测试后删除数据库，fixture会关闭文件并将其从文件系统中删除。&lt;/p&gt;
&lt;p&gt;如果我们现在运行测试组件，我们应该看到以下输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pytest tests/test_baby.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;========================================&lt;/span&gt; test session starts &lt;span style="color:#f92672"&gt;======================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;platform darwin -- Python 3.6.5, pytest-4.4.0, py-1.8.0, pluggy-0.9.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rootdir: /Users/durban/python/baby, inifile: setup.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;collected &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; items
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;====================================&lt;/span&gt; no tests ran in 0.05 seconds &lt;span style="color:#f92672"&gt;=================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即使它没有运行任何实际测试，我们已经知道我们的flaskr应用程序在语法上是有效的，否则导入将因异常而结束。&lt;/p&gt;
&lt;h3 id="测试数据库"&gt;测试数据库
&lt;/h3&gt;&lt;p&gt;现在是时候开始测试应用程序的功能了。如果我们访问应用程序的根目录（/），让我们检查应用程序是否显示“Baby”。为此，我们向test_baby.py添加了一个新的测试函数，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_empty_db&lt;/span&gt;(client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Baby&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，我们的测试函数以单词test开头;这允许pytest自动将函数识别为要运行的测试。&lt;/p&gt;
&lt;p&gt;通过使用&lt;code&gt;client.get&lt;/code&gt;，我们可以使用给定路径向应用程序发送HTTP &lt;code&gt;GET&lt;/code&gt;请求。返回值将是&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.response_class" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;response_class&lt;/strong&gt;&lt;/a&gt;对象。我们现在可以使用&lt;a class="link" href="http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.BaseResponse.data" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;data&lt;/strong&gt;&lt;/a&gt;属性来检查应用程序的返回值（作为字符串）。在这种情况下，我们确保“No entries here so far”是输出的一部分。&lt;/p&gt;
&lt;p&gt;再次运行它，您应该看到一个通过测试：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pytest -v tests/test_baby.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=======================&lt;/span&gt; test session starts &lt;span style="color:#f92672"&gt;========================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;platform darwin -- Python 3.6.5, pytest-4.4.0, py-1.8.0, pluggy-0.9.0 -- /Users/durban/python/baby/.env3/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cachedir: .pytest_cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rootdir: /Users/durban/python/baby, inifile: setup.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;collected &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_empty_db PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=====================&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; passed in 0.10 seconds &lt;span style="color:#f92672"&gt;=====================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="测试登录和注销"&gt;测试登录和注销
&lt;/h3&gt;&lt;p&gt;我们的应用程序的大部分功能仅适用于管理用户，因此我们需要一种方法来将我们的测试客户端记录到应用程序中。为此，我们使用所需的表单数据（用户名和密码）向登录和注销页面发出一些请求。并且由于登录和注销页面重定向，我们告诉客户端follow_redirects&lt;/p&gt;
&lt;p&gt;将以下类添加到test_baby.py文件中：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AuthActions&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;(self, username&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, password&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/auth/login&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;: username, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;: password}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;logout&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/auth/logout&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在，我们可以轻松地测试登录和注销是否正常工作，以及它是否因无效凭据而失败。添加这个新的测试功能：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_login&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login(&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; client:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; session[&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login(&lt;span style="color:#e6db74"&gt;&amp;#39;testx&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect username&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login(&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;testx&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect password&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_logout&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login(&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; client:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logout()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再次运行测试得到如下结果&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pytest -v tests/test_baby.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;===========================&lt;/span&gt; test session starts &lt;span style="color:#f92672"&gt;============================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;platform darwin -- Python 3.6.5, pytest-4.4.0, py-1.8.0, pluggy-0.9.0 -- /Users/durban/python/baby/.env3/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cachedir: .pytest_cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rootdir: /Users/durban/python/baby, inifile: setup.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;collected &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; items
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_empty_db PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 33%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_login PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 66%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_logout PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=========================&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; passed in 0.71 seconds &lt;span style="color:#f92672"&gt;=========================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="测试添加帖子"&gt;测试添加帖子
&lt;/h3&gt;&lt;p&gt;我们还应该测试添加帖子是否有效。添加一个新的测试函数，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_add_post&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; client:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/create&amp;#39;&lt;/span&gt;, data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dict(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;Hello&amp;gt;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;strong&amp;gt;Html&amp;lt;/strong&amp;gt; is here&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ), follow_redirects&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;amp;lt;strong&amp;amp;gt;Html&amp;amp;lt;/strong&amp;amp;gt; is here&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;amp;lt;Hello&amp;amp;gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这里，我们检查文本中是否允许HTML，而不是标题中的HTML，这是预期的行为。&lt;/p&gt;
&lt;p&gt;运行它现在应该给我们四个通过测试：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pytest -v tests/test_baby.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;===================&lt;/span&gt; test session starts &lt;span style="color:#f92672"&gt;====================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;platform darwin -- Python 3.6.5, pytest-4.4.0, py-1.8.0, pluggy-0.9.0 -- /Users/durban/python/baby/.env3/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cachedir: .pytest_cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rootdir: /Users/durban/python/baby, inifile: setup.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;collected &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; items
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_empty_db PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 25%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_login PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 50%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_logout PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 75%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_baby.py::test_add_post PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=================&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; passed in 0.71 seconds &lt;span style="color:#f92672"&gt;=================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="其他测试技巧"&gt;其他测试技巧
&lt;/h3&gt;&lt;p&gt;除了使用如上所示的测试client之外，还有&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_request_context" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;test_request_context()&lt;/strong&gt;&lt;/a&gt;方法，该方法可以与&lt;code&gt;with&lt;/code&gt;语句结合使用以临时激活请求上下文。通过这种方式，您可以访问&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request&lt;/strong&gt;&lt;/a&gt;，&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;g&lt;/strong&gt;&lt;/a&gt;和&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;session&lt;/strong&gt;&lt;/a&gt;对象，例如视图函数。以下是演示此方法的完整示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context(&lt;span style="color:#e6db74"&gt;&amp;#39;/?name=Peter&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args[&lt;span style="color:#e6db74"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Peter&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以以相同的方式使用上下文绑定的所有其他对象。&lt;/p&gt;
&lt;p&gt;如果要使用不同的配置测试应用程序并且似乎没有好的方法，请考虑切换到应用程序工厂（请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/patterns/appfactories/#app-factories" target="_blank" rel="noopener"
 &gt;应用程序工厂&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;但请注意，如果使用测试请求上下文，则不会自动调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt;和&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.after_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;after_request()&lt;/strong&gt;&lt;/a&gt;函数。但是，当测试请求上下文离开&lt;code&gt;with&lt;/code&gt;块时，确实执行了&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.teardown_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;teardown_request()&lt;/strong&gt;&lt;/a&gt;函数。如果你确实想要调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.before_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;before_request()&lt;/strong&gt;&lt;/a&gt;函数，你需要自己调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.preprocess_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;preprocess_request()&lt;/strong&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context(&lt;span style="color:#e6db74"&gt;&amp;#39;/?name=Peter&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;preprocess_request()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据应用程序的设计方式，这可能是打开数据库连接或类似连接所必需的。&lt;/p&gt;
&lt;p&gt;如果要调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.after_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;after_request()&lt;/strong&gt;&lt;/a&gt;函数，则需要调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.preprocess_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;preprocess_request()&lt;/strong&gt;&lt;/a&gt;，但是要求您将响应对象传递给它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context(&lt;span style="color:#e6db74"&gt;&amp;#39;/?name=Peter&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Response(&lt;span style="color:#e6db74"&gt;&amp;#39;...&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;process_response(resp)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这通常不太有用，因为此时您可以直接开始使用测试客户端。&lt;/p&gt;
&lt;h3 id="伪造资源和上下文"&gt;伪造资源和上下文
&lt;/h3&gt;&lt;p&gt;一种非常常见的模式是在应用程序上下文或flask.g对象上存储用户授权信息和数据库连接。一般的模式是在第一次使用时将对象放在那里，然后在拆卸时将其移除。想象一下这个代码来获取当前用户：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_user&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; getattr(g, &lt;span style="color:#e6db74"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; user &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fetch_current_user_from_database()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于测试，从外部覆盖此用户而不必更改某些代码将是很好的。这可以通过挂钩&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.appcontext_pushed" target="_blank" rel="noopener"
 &gt;flask.appcontext_pushed&lt;/a&gt;信号来完成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; contextlib &lt;span style="color:#f92672"&gt;import&lt;/span&gt; contextmanager
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; appcontext_pushed, g
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@contextmanager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user_set&lt;/span&gt;(app, user):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;handler&lt;/span&gt;(sender, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; appcontext_pushed&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connected_to(handler, app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后想下面这样使用它&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; json, jsonify
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/me&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;users_me&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; jsonify(username&lt;span style="color:#f92672"&gt;=&lt;/span&gt;g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;username)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; user_set(app, my_user):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; c:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/users/me&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loads(resp&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;assert_equal(data[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;], my_user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;username)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="保持上下文环境"&gt;保持上下文环境
&lt;/h3&gt;&lt;p&gt;有时触发常规请求保持上下文环境会有所帮助，但仍会将上下文保持一段时间，以便进行额外的内省。使用Flask 0.4，可以使用带有&lt;code&gt;with&lt;/code&gt;块的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_client" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;test_client()&lt;/strong&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; c:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/?tequila=42&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args[&lt;span style="color:#e6db74"&gt;&amp;#39;tequila&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;42&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您只使用不带with块的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_client" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;test_client()&lt;/strong&gt;&lt;/a&gt;，则断言将失败并显示错误，因为请求不再可用（因为您尝试在实际请求之外使用它）。&lt;/p&gt;
&lt;h3 id="访问和修改会话"&gt;访问和修改会话
&lt;/h3&gt;&lt;p&gt;有时，从测试客户端访问或修改会话非常有用。通常有两种方法。如果您只是想确保某个会话将某些键设置为某些值，您可以保持上下文并访问&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;flask.session&lt;/strong&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; c:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; flask&lt;span style="color:#f92672"&gt;.&lt;/span&gt;session[&lt;span style="color:#e6db74"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是，这无法在发出请求之前修改会话或访问会话。从Flask 0.8开始，我们提供了一个所谓的“会话事务”，它模拟在测试客户端上下文中打开会话的相应调用并对其进行修改。在事务结束时存储会话。这与所使用的会话后端无关：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; c:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;session_transaction() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; sess:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sess[&lt;span style="color:#e6db74"&gt;&amp;#39;a_key&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;a value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# once this is reached the session was stored&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，在这种情况下，您必须使用sess对象而不是flask.session代理。然而，对象本身将提供相同的接口。&lt;/p&gt;
&lt;h3 id="测试json-api"&gt;测试JSON API
&lt;/h3&gt;&lt;p&gt;Flask非常支持JSON，是构建JSON API的流行选择。使用JSON数据发出请求并检查响应中的JSON数据非常方便：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request, jsonify
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/auth&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; json_data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_json()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; email &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json_data[&lt;span style="color:#e6db74"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json_data[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; jsonify(token&lt;span style="color:#f92672"&gt;=&lt;/span&gt;generate_token(email, password))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; c:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/auth&amp;#39;&lt;/span&gt;, json&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;flask&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; json_data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; rv&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_json()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; verify_token(email, json_data[&lt;span style="color:#e6db74"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在测试客户端方法中传递&lt;code&gt;json&lt;/code&gt;参数将请求数据设置为JSON序列化对象，并将内容类型设置为&lt;code&gt;application/json&lt;/code&gt;。您可以使用&lt;code&gt;get_json&lt;/code&gt;从请求或响应中获取JSON数据。&lt;/p&gt;
&lt;h3 id="测试cli命令"&gt;测试CLI命令
&lt;/h3&gt;&lt;p&gt;Click附带了用于测试CLI命令的实用程序。CliRunner独立运行命令并捕获&lt;a class="link" href="http://click.pocoo.org/api/#click.testing.Result" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;/a&gt;对象中的输出。&lt;/p&gt;
&lt;p&gt;Flask提供了&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_cli_runner" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;test_cli_runner()&lt;/strong&gt;&lt;/a&gt;来创建&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.testing.FlaskCliRunner" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;FlaskCliRunner&lt;/strong&gt;&lt;/a&gt;，它自动将Flask应用程序传递给CLI。使用其&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.testing.FlaskCliRunner.invoke" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;invoke()&lt;/strong&gt;&lt;/a&gt;方法以与从命令行调用命令相同的方式调用命令。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; click
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.cli.command&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@click.option&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;--name&amp;#39;&lt;/span&gt;, default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;World&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello_command&lt;/span&gt;(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; click&lt;span style="color:#f92672"&gt;.&lt;/span&gt;echo(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Hello, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;!&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_hello&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runner &lt;span style="color:#f92672"&gt;=&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_cli_runner()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# invoke the command directly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; runner&lt;span style="color:#f92672"&gt;.&lt;/span&gt;invoke(hello_command, [&lt;span style="color:#e6db74"&gt;&amp;#39;--name&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;Flask&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, Flask&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# or by name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; runner&lt;span style="color:#f92672"&gt;.&lt;/span&gt;invoke(args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;World&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;output
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上面的示例中，按名称调用命令很有用，因为它验证命令是否已正确注册到应用程序。&lt;/p&gt;
&lt;p&gt;如果要测试命令如何解析参数而不运行命令，请使用其make_context()方法。这对于测试复杂的验证规则和自定义类型很有用。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;upper&lt;/span&gt;(ctx, param, value):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; value &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; value&lt;span style="color:#f92672"&gt;.&lt;/span&gt;upper()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.cli.command&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@click.option&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;--name&amp;#39;&lt;/span&gt;, default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;World&amp;#39;&lt;/span&gt;, callback&lt;span style="color:#f92672"&gt;=&lt;/span&gt;upper)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello_command&lt;/span&gt;(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; click&lt;span style="color:#f92672"&gt;.&lt;/span&gt;echo(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Hello, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;!&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_hello_params&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; hello_command&lt;span style="color:#f92672"&gt;.&lt;/span&gt;make_context(&lt;span style="color:#e6db74"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;, [&lt;span style="color:#e6db74"&gt;&amp;#39;--name&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;flask&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;params[&lt;span style="color:#e6db74"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;FLASK&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 进阶 - 模板</title><link>https://zhangdapeng.dev/p/flask-1-0-jin-jie-mu-ban/</link><pubDate>Tue, 29 Jul 2025 10:55:42 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-jin-jie-mu-ban/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 进阶 - 模板" /&gt;&lt;p&gt;Flask利用Jinja2作为模板引擎。你显然可以自由使用不同的模板引擎，但你仍然需要安装Jinja2来运行Flask本身。此要求是启用丰富扩展所必需的。扩展可以依赖于Jinja2存在。&lt;/p&gt;
&lt;p&gt;本节仅简要介绍Jinja2如何集成到Flask中。如果您需要有关模板引擎语法本身的信息，请访问&lt;a class="link" href="http://jinja.pocoo.org/docs/templates" target="_blank" rel="noopener"
 &gt;官方Jinja2模板文档&lt;/a&gt;以获取更多信息。&lt;/p&gt;
&lt;h3 id="jinja-设置"&gt;Jinja 设置
&lt;/h3&gt;&lt;p&gt;除非定制，否则Jinja2由Flask配置如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用render_template()时，对于以.html，.htm，.xml和.xhtml结尾的所有模板启用自动过滤。&lt;/li&gt;
&lt;li&gt;使用render_template_string()时，为所有字符串启用自动过滤。&lt;/li&gt;
&lt;li&gt;模板可以使用&lt;code&gt;{％autoescape％}&lt;/code&gt;标记选择使用火不适用自动过滤。&lt;/li&gt;
&lt;li&gt;除了默认存在的值之外，Flask还将一些全局函数和帮助程序插入到Jinja2上下文中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="标准上下文"&gt;标准上下文
&lt;/h3&gt;&lt;p&gt;默认情况下，Jinja2模板中提供以下全局变量：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;config&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;当前配置对象&lt;span style="color:#f92672"&gt;(&lt;/span&gt;**flask.config**&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;request&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;当前请求对象（**flask.request**）。如果在没有活动请求上下文的情况下呈现模板，则此变量不可用。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;session&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;当前session对象（**flask.session**）。如果在没有活动请求上下文的情况下呈现模板，则此变量不可用。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;g&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;全局变量的请求绑定对象（**flask.g**）。如果在没有活动请求上下文的情况下呈现模板，则此变量不可用。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;url_for()&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;**flask.url_for&lt;span style="color:#f92672"&gt;()&lt;/span&gt;**函数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;get_flash_messages()&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;**flask.get_flash_messages&lt;span style="color:#f92672"&gt;()&lt;/span&gt;**函数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Jinja上下文行为&lt;/p&gt;
&lt;p&gt;这些变量被添加到变量的上下文中，它们不是全局变量。不同之处在于，默认情况下这些不会显示在导入模板的上下文中。这部分是由性能考虑因素造成的，部分原因是为了使事情明确。&lt;/p&gt;
&lt;p&gt;这对你意味着什么？如果您有要导入的宏，则需要访问请求对象，您有两种可能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;您将request显式的传递给macro作为参数，或您感兴趣的请求对象的属性。&lt;/li&gt;
&lt;li&gt;你可以用&amp;quot;with context&amp;quot;导入macro。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;使用上下文导入如下所示：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{% from '_helpers.html' import my_macro with context %}&lt;/code&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="标准过滤器"&gt;标准过滤器
&lt;/h3&gt;&lt;p&gt;除了Jinja2本身提供的过滤器之外，Jinja2还提供了这些过滤器：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tojson&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;此函数将给定对象转换为JSON表示。例如，如果您尝试动态生成JavaScript，这将非常有用。&lt;/p&gt;
&lt;p&gt;请注意，内部脚本标记必须不进行转义，因此如果您打算在&lt;code&gt;script&lt;/code&gt;标记内使用它，请确保在Flask 0.10之前禁用转义&lt;code&gt;|safe&lt;/code&gt;;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;text/javascript&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;doSomethingWith&lt;/span&gt;({{ &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;tojson&lt;/span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;safe&lt;/span&gt; }});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="控制自动过滤"&gt;控制自动过滤
&lt;/h3&gt;&lt;p&gt;自动过滤是为您自动转义特殊字符的概念。HTML（或XML，因此XHTML）意义上的特殊字符是&lt;code&gt;＆&lt;/code&gt;，&lt;code&gt;&amp;gt;&lt;/code&gt;，&lt;code&gt;&amp;lt;&lt;/code&gt;，&lt;code&gt;'&lt;/code&gt;以及&lt;code&gt;&amp;quot;&lt;/code&gt;。因为这些字符本身在文档中具有特定含义，所以如果要将它们用于文本，则必须用所谓的“entities”替换它们。不这样做不仅会导致用户因无法在文本中使用这些字符而感到沮丧，而且还会导致安全问题。（请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/security/#xss" target="_blank" rel="noopener"
 &gt;跨站点脚本（XSS）&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;但有时您需要在模板中禁用自动转换。如果您想要将HTML显式地插入到页面中，例如，如果它们来自生成安全HTML的系统（如HTML转换器的markdown），则可能出现这种情况。&lt;/p&gt;
&lt;p&gt;有三种方法可以实现这一目标：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在Python代码中，将HTML字符串包装在Markup对象中，然后再将其传递给模板。这通常是推荐的方式。&lt;/li&gt;
&lt;li&gt;在模板内部，使用&lt;code&gt;|safe&lt;/code&gt;过滤器将字符串显式标记为安全HTML（&lt;code&gt;{{myvariable | safe}}&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;暂时禁用autoescape系统。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;要在模板中禁用autoescape系统，您可以使用&lt;code&gt;{％autoescape％}&lt;/code&gt;块：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% autoescape false %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;autoescaping is disabled here
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;{{ will_not_be_escaped }}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endautoescape %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;无论何时执行此操作，请对此块中使用的变量非常谨慎。&lt;/p&gt;
&lt;h3 id="注册过滤器"&gt;注册过滤器
&lt;/h3&gt;&lt;p&gt;如果您想在Jinja2中注册自己的过滤器，您有两种方法可以做到这一点。您可以手动将它们放入应用程序的jinja_env中，也可以使用template_filter())装饰器。&lt;/p&gt;
&lt;p&gt;以下两个示例的工作方式相同，都反转了一个对象：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.template_filter&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;reverse&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reverse_filter&lt;/span&gt;(s):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; s[::&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reverse_filter&lt;/span&gt;(s):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; s[::&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;jinja_env&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filters[&lt;span style="color:#e6db74"&gt;&amp;#39;reverse&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; reverse_filter
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在装饰器的情况下，如果要将函数名称用作过滤器的名称，则参数是可选的。注册后，您可以像使用Jinja2的内置过滤器一样在模板中使用过滤器，例如，如果您在上下文中有一个名为mylist的Python列表：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% for x in mylist | reverse %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endfor %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="上下文处理器"&gt;上下文处理器
&lt;/h3&gt;&lt;p&gt;要将新变量自动注入模板的上下文，Flask中存在上下文处理器。上下文处理器在呈现模板之前运行，并且能够将新值注入模板上下文。上下文处理器是一个返回字典的函数。然后，对于应用程序中的所有模板，此字典的键和值将与模板上下文合并：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.context_processor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;inject_user&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; dict(user&lt;span style="color:#f92672"&gt;=&lt;/span&gt;g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的上下文处理器在模板中创建了一个名为user的变量，其值为g.user。这个例子不是很有趣，因为无论如何g都可以在模板中使用，但它可以让我知道这是如何工作的。&lt;/p&gt;
&lt;p&gt;变量不限于数值;上下文处理器也可以使函数可用于模板（因为Python允许传递函数）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.context_processor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;utility_processor&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;format_price&lt;/span&gt;(amount, currency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;u&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;€&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;u&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{0:.2f}{1}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(amount, currency)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; dict(format_price&lt;span style="color:#f92672"&gt;=&lt;/span&gt;format_price)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的上下文处理器使format_price函数可用于所有模板：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{ format_price(0.33) }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;您还可以将format_price构建为模板过滤器（请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/templating/#registering-filters" target="_blank" rel="noopener"
 &gt;注册过滤器&lt;/a&gt;），但这演示了如何在上下文处理器中传递函数。&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 持续开发</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-chi-xu-kai-fa/</link><pubDate>Tue, 29 Jul 2025 10:55:39 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-chi-xu-kai-fa/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1560310227/walkerfree/flask.png" alt="Featured image of post Flask 1.0 新手教程 - 持续开发" /&gt;&lt;p&gt;你已经通过此教程学了一部分关于Flask和Python的概念。返回去再重新看下新手教程一步一步对比下你的代码，将您的项目与&lt;a class="link" href="https://github.com/pallets/flask/tree/1.0.2/examples/tutorial" target="_blank" rel="noopener"
 &gt;示例项目&lt;/a&gt;进行比较，由于本教程的逐步特性，该项目可能看起来有点不同&lt;/p&gt;
&lt;p&gt;Flask比你到目前为止看到的要多得多。即便如此，您现在已经准备好开始开发自己的Web应用程序了。查看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/quickstart/#quickstart" target="_blank" rel="noopener"
 &gt;快速入门&lt;/a&gt;，了解Flask可以做些什么，然后深入了解文档以继续学习。Flask在幕后使用&lt;a class="link" href="https://palletsprojects.com/p/jinja/" target="_blank" rel="noopener"
 &gt;Jinja&lt;/a&gt;，&lt;a class="link" href="https://palletsprojects.com/p/click/" target="_blank" rel="noopener"
 &gt;Click&lt;/a&gt;，&lt;a class="link" href="https://palletsprojects.com/p/werkzeug/" target="_blank" rel="noopener"
 &gt;Werkzeug&lt;/a&gt;和&lt;a class="link" href="https://palletsprojects.com/p/itsdangerous/" target="_blank" rel="noopener"
 &gt;ItsDangerous&lt;/a&gt;，它们都有自己的文档。您还会对&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/extensions/#extensions" target="_blank" rel="noopener"
 &gt;Extensions&lt;/a&gt;感兴趣，它可以使用数据库或验证表单数据等任务变得更容易，更强大。&lt;/p&gt;
&lt;p&gt;如果您想继续开发Baby项目，可以参考以下内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个展示帖子详情的视图，点击帖子的标题跳转到这个页面&lt;/li&gt;
&lt;li&gt;喜欢/不喜欢帖子&lt;/li&gt;
&lt;li&gt;评论&lt;/li&gt;
&lt;li&gt;标签。点击一个标签显示所有关于这个标签的帖子&lt;/li&gt;
&lt;li&gt;一个搜索框，能够通过名称过滤帖子&lt;/li&gt;
&lt;li&gt;分页。一个页面只显示5个帖子&lt;/li&gt;
&lt;li&gt;上传图片以配合帖子。&lt;/li&gt;
&lt;li&gt;使用Markdown格式化帖子。&lt;/li&gt;
&lt;li&gt;新帖子的RSS提要。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;玩得开心，制作更加出色的应用程序！&lt;/p&gt;
&lt;p&gt;完结。&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 部署到生产环境</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-bu-shu-dao-sheng-chan-huan-jing/</link><pubDate>Tue, 29 Jul 2025 10:55:35 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-bu-shu-dao-sheng-chan-huan-jing/</guid><description>&lt;p&gt;这部分假设你有一台可以部署你应用的服务器，它概述了如何创建分发文件并进行安装，但不会详细介绍要使用的服务器或软件。您可以在开发计算机上设置新环境以尝试以下说明，但可能不应将其用于托管真实的公共应用程序。有关托管应用程序的许多不同方法的列表，请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/deploying/" target="_blank" rel="noopener"
 &gt;部署选项&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="构建和安装"&gt;构建和安装
&lt;/h3&gt;&lt;p&gt;如果要在其他位置部署应用程序，可以构建分发文件。Python发行版的当前标准是&lt;em&gt;wheel&lt;/em&gt;格式，扩展名为.whl。确保首先安装了wheel库。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install wheel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用Python运行&lt;code&gt;setup.py&lt;/code&gt;为您提供了一个命令行工具来发出与构建相关的命令。bdist_wheel命令将构建一个wheel分配文件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python setup.py bdist_wheel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行完之后，将会在目录dist下发现一个文件&lt;code&gt;baby-1.0.0-py3-none-any.whl&lt;/code&gt;。文件名是项目的名称，版本以及有关该文件的一些标记可以安装。&lt;/p&gt;
&lt;p&gt;将此文件复制到另一台计算机，&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/installation/#install-create-env" target="_blank" rel="noopener"
 &gt;设置一个新的virtualenv&lt;/a&gt;，然后使用pip安装该文件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install baby-1.0.0-py3-none-any.whl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pip将安装您的项目及其依赖项。&lt;/p&gt;
&lt;p&gt;由于这是一台不同的机器，因此需要再次运行&lt;code&gt;init-db&lt;/code&gt;以在instance文件夹中创建数据库。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask init-db
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当Flask检测到它已安装（不是可编辑模式）时，它会为instance文件夹使用不同的目录。你可以在&lt;code&gt;venv/var/baby-instance&lt;/code&gt;找到它。&lt;/p&gt;
&lt;h3 id="配置私钥secret-key"&gt;配置私钥（Secret Key）
&lt;/h3&gt;&lt;p&gt;在本教程的第一篇分享中，为SECRET_KEY提供了默认值。生产环境中应该改为一些随机字节。否则，攻击者可以使用公共“dev”密钥来修改会话cookie或使用密钥的任何其他内容。&lt;/p&gt;
&lt;p&gt;您可以使用以下命令输出随机密钥：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -c &lt;span style="color:#e6db74"&gt;&amp;#39;import os; print(os.urandom(16))&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b&lt;span style="color:#e6db74"&gt;&amp;#39;\x05\x81\xb5\xa2xln&amp;amp;\\\xb8\xda\xecY\x94\xef\xb5&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在instance文件夹中创建config.py文件，如果存在，工厂将从中读取该文件。将生成的值复制到其中。&lt;/p&gt;
&lt;p&gt;venv/var/baby-instance/config.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SECRET_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;b&lt;span style="color:#e6db74"&gt;&amp;#39;\x05\x81\xb5\xa2xln&amp;amp;\\\xb8\xda\xecY\x94\xef\xb5&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你也可以在这里设置任何其他必要的配置，虽然SECRET_KEY是Baby唯一需要的配置。&lt;/p&gt;
&lt;h3 id="运行生产环境服务器"&gt;运行生产环境服务器
&lt;/h3&gt;&lt;p&gt;在生产环境中而不是在开发环境中运行时，不应使用内置开发服务器（&lt;code&gt;flask run&lt;/code&gt;）。开发服务器由Werkzeug提供以方便使用，但其设计不是特别高效，稳定或安全。&lt;/p&gt;
&lt;p&gt;而是使用生产WSGI服务器。例如，要使用&lt;a class="link" href="https://docs.pylonsproject.org/projects/waitress/" target="_blank" rel="noopener"
 &gt;Waitress&lt;/a&gt;，请先在虚拟环境中安装它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install waitress
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;您需要告诉Waitress您的应用程序，但它不像Flask运行那样使用FLASK_APP。您需要告诉它导入并调用应用程序工厂以获取应用程序对象。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;waitress-serve --call &lt;span style="color:#e6db74"&gt;&amp;#39;baby:create_app&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Serving on http://0.0.0.0:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有关托管应用程序的许多不同方法的列表，请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/deploying/" target="_blank" rel="noopener"
 &gt;部署选项&lt;/a&gt;。Waitress只是一个例子，因为它同时支持Windows和Linux。您可以为项目选择更多WSGI服务器和部署选项。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/162" target="_blank" rel="noopener"
 &gt;继续开发&lt;/a&gt;！&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 测试覆盖率</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-ce-shi-fu-gai-lv/</link><pubDate>Tue, 29 Jul 2025 10:55:32 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-ce-shi-fu-gai-lv/</guid><description>&lt;p&gt;为我们开发的程序写单元测试是为了检查我们写的代码能够按照我们希望的逻方式去运行。Flask提供了一个测试客户端来模拟请求到应用程序然后返回响应的数据。&lt;/p&gt;
&lt;p&gt;您应该尽可能多地测试代码。函数中的代码仅在调用函数时运行，分支中的代码（如&lt;code&gt;if&lt;/code&gt;块）仅在满足条件时运行。您希望确保使用涵盖每个分支的数据测试每个函数。&lt;/p&gt;
&lt;p&gt;您越接近100％覆盖率，您就越容易做出改变,而这个改变不会意外地改变其他行为。但是，100％的覆盖率并不能保证您的应用程序没有错误。特别是，它不能监测到用户如何在浏览器中与应用程序交互。尽管如此，测试覆盖率是开发过程中使用的重要工具。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="注意"&gt;注意
&lt;/h3&gt;&lt;p&gt;这是在本教程后期介绍的，但在您将来的项目中，您应该在开发时进行测试。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;您将使用pytest和coverage来测试和测量代码，安装命令如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install pytest coverage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="setup和fixtures"&gt;Setup和Fixtures
&lt;/h2&gt;&lt;p&gt;测试代码放在了&lt;code&gt;tests&lt;/code&gt;目录中，这个目录跟baby包同层级，不在包里面。tests/conftest.py文件包含了设置函数。&lt;code&gt;tests/conftest.py&lt;/code&gt;文件包含每个测试将使用的称为&lt;em&gt;fixture&lt;/em&gt;的设置函数。测试是在以&lt;code&gt;test_&lt;/code&gt;开头的Python模块中，并且这些模块中的每个测试函数也以&lt;code&gt;test_&lt;/code&gt;开头。&lt;/p&gt;
&lt;p&gt;每个测试都将创建一个新的临时数据库文件，并填充将在测试中使用的一些数据。编写一个SQL文件来插入该数据。&lt;/p&gt;
&lt;p&gt;tests/data.sql&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;user&lt;/span&gt; (username, password)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;other&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; post (title, body, author_id, created)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;test title&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; x&lt;span style="color:#e6db74"&gt;&amp;#39;0a&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;2019-01-01 00:00:00&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;应用程序fixture将调用工厂并传递&lt;code&gt;test_config&lt;/code&gt;以配置应用程序和数据库以进行测试，而不是使用本地开发配置。&lt;/p&gt;
&lt;p&gt;tests/conftest.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; tempfile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; get_db, init_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; open(os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dirname(__file__), &lt;span style="color:#e6db74"&gt;&amp;#39;data.sql&amp;#39;&lt;/span&gt;), &lt;span style="color:#e6db74"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _data_sql &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;decode(&lt;span style="color:#e6db74"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db_fd, db_path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tempfile&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkstemp()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_app({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;TESTING&amp;#39;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;DATABASE&amp;#39;&lt;/span&gt;: db_path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; init_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; get_db()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;executescript(_data_sql)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close(db_fd)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(db_path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;(app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_client()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;runner&lt;/span&gt;(app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_cli_runner()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="https://docs.python.org/3/library/tempfile.html#tempfile.mkstemp" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;tempfile.mkstemp()&lt;/strong&gt;&lt;/a&gt;创建并打开一个临时文件，返回文件对象及其路径。重写DATABASE路径，因此它指向此临时路径而不是实例文件夹。设置路径后，将创建数据库表并插入测试数据。测试结束后，临时文件将被关闭并删除。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/config/#TESTING" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;TESTING&lt;/strong&gt;&lt;/a&gt;告诉Flask应用程序处于测试模式。Flask更改了一些内部行为，因此更容易测试，而其他扩展也可以使用该标志来更轻松地测试它们。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;client&lt;/code&gt;fixture使用&lt;code&gt;app&lt;/code&gt;fixture创建的应用程序对象调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_client" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.test_client()&lt;/strong&gt;&lt;/a&gt;。测试将使用客户端向应用程序发出请求而不运行服务器。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;runner&lt;/code&gt;fixture与&lt;code&gt;client&lt;/code&gt;类似。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_cli_runner" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.test_cli_runner()&lt;/strong&gt;&lt;/a&gt;创建一个可以调用在应用程序中注册的Click命令的runner。&lt;/p&gt;
&lt;p&gt;Pytest通过将其函数名称与测试函数中的参数名称进行匹配来使用fixture。例如，您接下来要编写的test_hello函数将使用&lt;code&gt;client&lt;/code&gt;参数。Pytest与&lt;code&gt;client&lt;/code&gt;fixture函数匹配，调用它，并将返回的值传递给测试函数。&lt;/p&gt;
&lt;h2 id="工厂"&gt;工厂
&lt;/h2&gt;&lt;p&gt;关于工厂本身的测试并不多。大多数代码都将针对每个测试执行，因此如果某些内容失败，其他测试将会受到影响。&lt;/p&gt;
&lt;p&gt;唯一可以改变的行为是传递测试配置。如果未传递config，则应该有一些默认配置，否则应该重写配置。&lt;/p&gt;
&lt;p&gt;tests/test_factory.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_config&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; create_app()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;testing
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; create_app({&lt;span style="color:#e6db74"&gt;&amp;#39;TESTING&amp;#39;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;})&lt;span style="color:#f92672"&gt;.&lt;/span&gt;testing
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_baby&lt;/span&gt;(client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/baby&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Baby&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在本教程开头编写工厂时，您添加了baby路由作为示例。它返回“Baby”，因此测试检查响应数据是否匹配。&lt;/p&gt;
&lt;h2 id="数据库"&gt;数据库
&lt;/h2&gt;&lt;p&gt;在应用程序上下文中，&lt;code&gt;get_db&lt;/code&gt;应在每次调用时返回相同的连接。在上下文之后，应该关闭连接。&lt;/p&gt;
&lt;p&gt;tests/test_db.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; sqlite3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; get_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_get_close_db&lt;/span&gt;(app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; db &lt;span style="color:#f92672"&gt;is&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; pytest&lt;span style="color:#f92672"&gt;.&lt;/span&gt;raises(sqlite3&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ProgrammingError) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT 1&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;closed&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; str(e)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;init-db&lt;/code&gt;命令应调用&lt;code&gt;init_db&lt;/code&gt;函数并输出消息。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_init_db_command&lt;/span&gt;(runner, monkeypatch):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Recorder&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; called &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fake_init_db&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Recorder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;called &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; monkeypatch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setattr(&lt;span style="color:#e6db74"&gt;&amp;#39;baby.db.init_db&amp;#39;&lt;/span&gt;, fake_init_db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; runner&lt;span style="color:#f92672"&gt;.&lt;/span&gt;invoke(args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;init-db&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Initialized&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; Recorder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;called
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此测试使用Pytest的&lt;code&gt;monkeypatch&lt;/code&gt;fixture将&lt;code&gt;init_db&lt;/code&gt;函数替换为记录它已被调用的函数。您在上面编写的&lt;code&gt;runner&lt;/code&gt;命令用于按名称调用&lt;code&gt;init-db&lt;/code&gt;命令。&lt;/p&gt;
&lt;h2 id="认证"&gt;认证
&lt;/h2&gt;&lt;p&gt;对于大多数视图，用户需要登录。在测试中执行此操作的最简单方法是使用客户端向&lt;code&gt;login&lt;/code&gt;视图发出&lt;code&gt;POST&lt;/code&gt;请求。您可以编写一个包含方法的类，而不是每次都写出来，并使用fixture将每个测试的客户端传递给它。&lt;/p&gt;
&lt;p&gt;tests/conftest.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AuthActions&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;(self, username&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, password&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/auth/login&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;: username, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;: password}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;logout&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/auth/logout&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth&lt;/span&gt;(client):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; AuthActions(client)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用&lt;code&gt;auth&lt;/code&gt;fixture，您可以在测试中调用&lt;code&gt;auth.login()&lt;/code&gt;以作为测试用户登录，该测试用户作为测试数据的一部分插入到app fixture中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;register&lt;/code&gt;视图应在&lt;code&gt;GET&lt;/code&gt;上成功渲染。在具有有效表单数据的POST上，它应该重定向到登录URL，并且用户的数据应该在数据库中。无效数据应显示错误消息。&lt;/p&gt;
&lt;p&gt;tests/test_auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; g, session
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; get_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_register&lt;/span&gt;(client, app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/auth/register&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/auth/register&amp;#39;&lt;/span&gt;, data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;http://localhost/auth/login&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#39;Location&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; get_db()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;SELECT * FROM user WHERE username = &amp;#39;a&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone() &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.parametrize&lt;/span&gt;((&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;), (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Username is required&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Password is required&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;already registered&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_register_validate_input&lt;/span&gt;(client, username, password, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/auth/register&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;:username, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;: password}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; message &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://werkzeug.pocoo.org/docs/test/#werkzeug.test.Client.get" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;client.get()&lt;/strong&gt;&lt;/a&gt;发出&lt;code&gt;GET&lt;/code&gt;请求并返回Flask返回的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Response" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Response&lt;/strong&gt;&lt;/a&gt;对象。类似地，&lt;a class="link" href="http://werkzeug.pocoo.org/docs/test/#werkzeug.test.Client.post" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;client.post()&lt;/strong&gt;&lt;/a&gt;发出&lt;code&gt;POST&lt;/code&gt;请求，将&lt;code&gt;data&lt;/code&gt;字典转换为表单数据。&lt;/p&gt;
&lt;p&gt;要测试页面是否渲染成功，可以发出一个简单的请求并检查&lt;code&gt;200 OK&lt;/code&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Response.status_code" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;状态码&lt;/strong&gt;&lt;/a&gt;。如果渲染失败，Flask将返回&lt;code&gt;500内部服务器错误代码&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当注册视图重定向到登录视图时，&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Response.headers" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;header&lt;/strong&gt;&lt;/a&gt;将是一个带有登录URL的&lt;code&gt;Location&lt;/code&gt;标头。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Response.data" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;data&lt;/strong&gt;&lt;/a&gt;包含响应的主体作为字节。如果您希望在页面上呈现某个值，请检查它是否在数据中。必须将字节与字节进行比较。如果要比较Unicode文本，请改用&lt;a class="link" href="http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.BaseResponse.get_data" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;get_data(as_text = True)&lt;/strong&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pytest.mark.parametrize&lt;/code&gt;告诉Pytest使用不同的参数运行相同的测试函数。您可以在此处使用它来测试不同的无效输入和错误消息，而无需编写相同的代码三次。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;login&lt;/code&gt;视图的测试与&lt;code&gt;register&lt;/code&gt;的测试非常相似。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;session&lt;/strong&gt;&lt;/a&gt;应该在登录后设置&lt;code&gt;user_id&lt;/code&gt;，而不是测试数据库中的数据。&lt;/p&gt;
&lt;p&gt;tests/test_auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_login&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/auth/login&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;http://localhost/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#39;Location&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; client:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; session[&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.parametrize&lt;/span&gt;((&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;),(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect username.&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect password.&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_login_validate_input&lt;/span&gt;(auth, username, password, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login(username, password)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; message &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在with块中使用client允许在返回响应后访问上下文变量，例如&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;session&lt;/strong&gt;&lt;/a&gt;。通常，访问请求之外的&lt;code&gt;session&lt;/code&gt;会引发错误。&lt;/p&gt;
&lt;p&gt;测试&lt;code&gt;logout&lt;/code&gt;与&lt;code&gt;login&lt;/code&gt;相反。注销后，&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;session&lt;/strong&gt;&lt;/a&gt;不应包含&lt;code&gt;user_id&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;tests/test_auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_logout&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; client:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logout()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="博客"&gt;博客
&lt;/h2&gt;&lt;p&gt;所有博客视图都使用之前编写的&lt;code&gt;auth&lt;/code&gt; fixture。调用&lt;code&gt;auth.login()&lt;/code&gt;以及来自客户端的后续请求将作为测试用户登录。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;index&lt;/code&gt;视图应显示有关随测试数据添加的帖子的信息。以作者身份登录时，应该有一个链接来编辑帖子。&lt;/p&gt;
&lt;p&gt;还可以在测试&lt;code&gt;index&lt;/code&gt;视图时测试更多身份验证行为。未登录时，每个页面都会显示登录或注册的链接。登录后，会有一个注销链接。&lt;/p&gt;
&lt;p&gt;tests/test_blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; get_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_index&lt;/span&gt;(client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Log In&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Register&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Log Out&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test title&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;by test on 2019-01-01&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;test&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;body&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;href=&amp;#34;/1/update&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;create&lt;/code&gt;,&lt;code&gt;update&lt;/code&gt;,&lt;code&gt;delete&lt;/code&gt;视图必须在用户登录后才能访问，&lt;code&gt;update&lt;/code&gt;和&lt;code&gt;delete&lt;/code&gt;视图必须在登录的用户是帖子的作者时才能被访问，否则返回&lt;code&gt;403 Forbidden&lt;/code&gt;状态。如果post请求是给传递的id不存在，则&lt;code&gt;update&lt;/code&gt;和&lt;code&gt;delete&lt;/code&gt;视图应该返回&lt;code&gt;404 Not Found&lt;/code&gt;状态。&lt;/p&gt;
&lt;p&gt;tests/test_blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.parametrize&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;, (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/create&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/1/update&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/1/delete&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_login_required&lt;/span&gt;(client, path):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;http://localhost/auth/login&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#39;Location&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_author_required&lt;/span&gt;(app, client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;UPDATE post SET author_id = 2 WHERE id = 1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/1/update&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;403&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/1/delete&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;403&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;href=&amp;#34;/1/update&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.parametrize&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;, (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;/2/update&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;/2/delete&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_exists_required&lt;/span&gt;(client, auth, path):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(path)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;create&lt;/code&gt;和&lt;code&gt;update&lt;/code&gt;视图在&lt;code&gt;GET&lt;/code&gt;请求时应该渲染并返回&lt;code&gt;200 OK&lt;/code&gt;状态。当有效的数据通过&lt;code&gt;POST&lt;/code&gt;请求提交时，&lt;code&gt;create&lt;/code&gt;视图应该输入新的数据到数据库，&lt;code&gt;update&lt;/code&gt;视图应该修改存在的数据。两个视图在遇到无效的数据提交时应该显示错误信息。&lt;/p&gt;
&lt;p&gt;tests/test_blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_create&lt;/span&gt;(client, auth, app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/create&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/create&amp;#39;&lt;/span&gt;, data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;created&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT COUNT(id) FROM post&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; count &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_update&lt;/span&gt;(app, client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;/1/update&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/1/update&amp;#39;&lt;/span&gt;, data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;updated&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM post WHERE id = 1&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; post[&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;updated&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.parametrize&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;, (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/create&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/1/update&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_create_update_validate&lt;/span&gt;(client, auth, path):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(path, data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Title is required&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;delete&lt;/code&gt;视图应该重新到index URL，并且数据库不应该存在该帖子。&lt;/p&gt;
&lt;p&gt;tests/test_blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_delete&lt;/span&gt;(app, client, auth):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;login()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;&amp;#39;/1/delete&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#39;Location&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;http://localhost/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM post WHERE id = 1&amp;#39;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; post &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="运行测试"&gt;运行测试
&lt;/h2&gt;&lt;p&gt;可以将一些额外的配置添加到项目的&lt;code&gt;setup.cfg&lt;/code&gt;文件中，这些配置不是必需的，但可以使运行测试的覆盖范围更加冗长。&lt;/p&gt;
&lt;p&gt;setup.cfg&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;tool:pytest&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;testpaths &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tests
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;coverage:run&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;branch &lt;span style="color:#f92672"&gt;=&lt;/span&gt; True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要运行测试，请使用pytest命令。它将查找并运行我们编写的所有测试函数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;==============================&lt;/span&gt; test session starts &lt;span style="color:#f92672"&gt;==============================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;platform darwin -- Python 3.6.5, pytest-4.4.0, py-1.8.0, pluggy-0.9.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rootdir: /Users/durban/python/baby, inifile: setup.cfg, testpaths: tests
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;collected &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt; items
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py ........ &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 33%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py ............ &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 83%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_db.py .. &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 91%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_factory.py .. &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;===========================&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt; passed in 1.83 seconds &lt;span style="color:#f92672"&gt;===========================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;.env3&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果任何测试失败，pytest将显示引发的错误。您可以运行pytest -v来获取每个测试函数的列表而不是点。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pytest -v
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=====================================&lt;/span&gt; test session starts &lt;span style="color:#f92672"&gt;======================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;platform darwin -- Python 3.6.5, pytest-4.4.0, py-1.8.0, pluggy-0.9.0 -- /Users/durban/python/baby/.env3/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cachedir: .pytest_cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rootdir: /Users/durban/python/baby, inifile: setup.cfg, testpaths: tests
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;collected &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt; items
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_register PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 4%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_register_validate_input&lt;span style="color:#f92672"&gt;[&lt;/span&gt;--Username is required&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 8%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_register_validate_input&lt;span style="color:#f92672"&gt;[&lt;/span&gt;a--Password is required&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 12%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_register_validate_input&lt;span style="color:#f92672"&gt;[&lt;/span&gt;test-test-already registered&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 16%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_login PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 20%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_login_validate_input&lt;span style="color:#f92672"&gt;[&lt;/span&gt;a-test-Incorrect username.&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 25%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_login_validate_input&lt;span style="color:#f92672"&gt;[&lt;/span&gt;test-a-Incorrect password.&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 29%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_auth.py::test_logout PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 33%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_index PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 37%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_login_required&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/create&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 41%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_login_required&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/1/update&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 45%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_login_required&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/1/delete&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 50%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_author_required PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 54%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_exists_required&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/2/update&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 58%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_exists_required&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/2/delete&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 62%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_create PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 66%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_update PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 70%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_create_update_validate&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/create&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 75%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_create_update_validate&lt;span style="color:#f92672"&gt;[&lt;/span&gt;/1/update&lt;span style="color:#f92672"&gt;]&lt;/span&gt; PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 79%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_blog.py::test_delete PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 83%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_db.py::test_get_close_db PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 87%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_db.py::test_init_db_command PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 91%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_factory.py::test_config PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt; 95%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tests/test_factory.py::test_baby PASSED &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100%&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;==================================&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt; passed in 1.94 seconds &lt;span style="color:#f92672"&gt;===================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;.env3&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要测量测试的代码覆盖率，请使用coverage命令运行pytest而不是直接运行它。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;coverage run -m pytest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;您可以在终端中查看简单的覆盖率报告：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;coverage report
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ coverage report
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Name Stmts Miss Branch BrPart Cover
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby/__init__.py &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 93%
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby/auth.py &lt;span style="color:#ae81ff"&gt;54&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 100%
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby/blog.py &lt;span style="color:#ae81ff"&gt;54&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 100%
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby/db.py &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 100%
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TOTAL &lt;span style="color:#ae81ff"&gt;157&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;46&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 99%
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;.env3&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;HTML报告允许您查看每个文件中包含的行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;coverage html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这会在&lt;code&gt;htmlcov&lt;/code&gt;目录中生成文件。在浏览器中打开&lt;code&gt;htmlcov/index.html&lt;/code&gt;以查看报告。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/161" target="_blank" rel="noopener"
 &gt;部署到生产&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 使项目可安装</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-shi-xiang-mu-ke-an-zhuang/</link><pubDate>Tue, 29 Jul 2025 10:09:53 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-shi-xiang-mu-ke-an-zhuang/</guid><description>&lt;p&gt;使项目可安装意味着您可以构建分发文件并将其安装在另一个环境中，就像在项目环境中安装Flask一样。这使得部署项目与安装任何其他库相同，因此您使用所有标准Python工具来管理所有内容。&lt;/p&gt;
&lt;p&gt;安装还带来了其他一些好处，这些好处可能在教程中或作为新的Python用户不明显，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目前，Python和Flask只了解如何使用baby包，因为你是从项目目录运行的。安装意味着无论您从何处运行，都可以导入它。&lt;/li&gt;
&lt;li&gt;您可以像管理其他软件包一样管理项目的依赖项，因此请安装yourproject.whl安装它们。&lt;/li&gt;
&lt;li&gt;测试工具可以将您的测试环境与开发环境隔离开来。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="注意"&gt;注意
&lt;/h3&gt;&lt;p&gt;这是在本教程后期介绍的，但是在您将来的项目中，您应该始终从这开始。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="项目描述"&gt;项目描述
&lt;/h2&gt;&lt;p&gt;setup.py文件描述了您的项目以及属于它的文件。&lt;/p&gt;
&lt;p&gt;setup.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; setuptools &lt;span style="color:#f92672"&gt;import&lt;/span&gt; find_packages, setup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;setup(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;baby&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;1.0.0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;张大鹏&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author_email&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;[email protected]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; packages&lt;span style="color:#f92672"&gt;=&lt;/span&gt;find_packages(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; include_package_data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zip_safe&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; install_requires&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;flask&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;packages&lt;/code&gt;告诉Python要包含哪些包目录（以及它们包含的Python文件）。&lt;code&gt;find_packages()&lt;/code&gt;自动查找这些目录，因此您不必键入它们。要包含其他文件，例如static和templates目录，请设置&lt;code&gt;include_package_data&lt;/code&gt;。Python需要另一个名为&lt;code&gt;MANIFEST.in&lt;/code&gt;的文件来告诉其他数据是什么。&lt;/p&gt;
&lt;p&gt;MANIFEST.in&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;include baby/schema.sql
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;graft baby/static
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;graft baby/templates
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;global-exclude *.pyc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这告诉Python复制&lt;code&gt;static&lt;/code&gt;和&lt;code&gt;template&lt;/code&gt;目录以及&lt;code&gt;schema.sql&lt;/code&gt;文件中的所有内容，但要排除所有字节码文件。&lt;/p&gt;
&lt;p&gt;有关所用文件和选项的其他说明，请参阅&lt;a class="link" href="https://packaging.python.org/tutorials/distributing-packages/" target="_blank" rel="noopener"
 &gt;官方包装指南&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="安装项目"&gt;安装项目
&lt;/h2&gt;&lt;p&gt;使用pip在虚拟环境中安装项目。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install -e .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这告诉pip在当前目录中找到setup.py并将其安装在可编辑或开发模式下。可编辑模式意味着当您对本地代码进行更改时，如果更改有关项目的元数据（例如其依赖项），则只需重新安装。&lt;/p&gt;
&lt;p&gt;您可以观察到项目现在已安装了&lt;code&gt;pip list&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pip list
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Package Version Location
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------- ------- -------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;astroid 2.2.5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;atomicwrites 1.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;attrs 19.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;autopep8 1.4.3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baby 1.0.0 /Users/durban/python/baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Click 7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;coverage 4.5.3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Flask 1.0.2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gunicorn 19.9.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;isort 4.3.16
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;itsdangerous 1.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Jinja2 2.10
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;lazy-object-proxy 1.3.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MarkupSafe 1.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mccabe 0.6.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;more-itertools 7.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip 19.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pluggy 0.9.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;py 1.8.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pycodestyle 2.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pylint 2.3.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pytest 4.4.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;setuptools 39.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;six 1.12.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;typed-ast 1.3.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;waitress 1.2.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Werkzeug 0.15.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wheel 0.33.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wrapt 1.11.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到目前为止，您运行项目的方式没有任何变化。&lt;code&gt;FLASK_APP&lt;/code&gt;仍然设置为&lt;code&gt;baby&lt;/code&gt;并且&lt;code&gt;flask run&lt;/code&gt;仍然运行应用程序。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/160" target="_blank" rel="noopener"
 &gt;测试覆盖率&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 博客Blueprint</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-bo-ke-blueprint/</link><pubDate>Tue, 29 Jul 2025 10:09:50 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-bo-ke-blueprint/</guid><description>&lt;p&gt;写博客的Blueprint跟写身份认证的Blueprint所使用的技术是一样的。博客应该列出所有的帖子，允许登录的用户去创建帖子，允许发帖用户去编辑或者删除帖子。&lt;/p&gt;
&lt;p&gt;当我们实现每个视图的时候，请保持服务器一直处于运行状态。在保存更改时，请尝试访问浏览器中的URL并进行测试。&lt;/p&gt;
&lt;h2 id="blueprint"&gt;Blueprint
&lt;/h2&gt;&lt;p&gt;在应用程序工厂中定义blueprint并注册它&lt;/p&gt;
&lt;p&gt;baby/blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Blueprint, flash, g, redirect, request, render_template, url_for
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.exceptions &lt;span style="color:#f92672"&gt;import&lt;/span&gt; abort
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.auth &lt;span style="color:#f92672"&gt;import&lt;/span&gt; login_required
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; get_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Blueprint(&lt;span style="color:#e6db74"&gt;&amp;#39;blog&amp;#39;&lt;/span&gt;, __name__)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.register_blueprint" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.register_blueprint()&lt;/strong&gt;&lt;/a&gt;在工厂中导入并注册blueprint。放下面的代码在工厂的底部，在返回变量app之前。&lt;/p&gt;
&lt;p&gt;baby/&lt;strong&gt;init&lt;/strong&gt;.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;(test_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 忽略已经有的部分代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; . &lt;span style="color:#f92672"&gt;import&lt;/span&gt; blog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register_blueprint(blog&lt;span style="color:#f92672"&gt;.&lt;/span&gt;bp)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_url_rule(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, endpoint&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不像身份认证blueprint，博客blueprint没有使用&lt;code&gt;url_prefix&lt;/code&gt;，因此index视图将被放在&lt;code&gt;/&lt;/code&gt;路由上，&lt;code&gt;create&lt;/code&gt;视图位于&lt;code&gt;/create&lt;/code&gt;，依此类推。博客是Baby的主要特色，因此博客索引将成为主要索引。&lt;/p&gt;
&lt;p&gt;但是，下面定义的&lt;code&gt;index&lt;/code&gt;视图的endpoint将是&lt;code&gt;blog.index&lt;/code&gt;。一些身份验证视图引用了普通&lt;code&gt;index&lt;/code&gt;endpoint。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.add_url_rule" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.add_url_rule()&lt;/strong&gt;&lt;/a&gt;将endpoint名称为&lt;code&gt;'index'&lt;/code&gt;与&lt;code&gt;/&lt;/code&gt;url相关联，以便&lt;code&gt;url_for('index')&lt;/code&gt;或&lt;code&gt;url_for('blog.index')&lt;/code&gt;都可以工作，生成相同&lt;code&gt;/&lt;/code&gt;URL的方式。&lt;/p&gt;
&lt;p&gt;在另一个应用程序中，您可以为博客blueprint提供&lt;code&gt;url_prefix&lt;/code&gt;，并在应用程序工厂中定义单独的&lt;code&gt;index&lt;/code&gt;视图，类似于&lt;code&gt;hello&lt;/code&gt;视图。然后&lt;code&gt;index&lt;/code&gt;和&lt;code&gt;blog.index&lt;/code&gt;endpoint和URL会有所不同。&lt;/p&gt;
&lt;h2 id="index视图"&gt;Index视图
&lt;/h2&gt;&lt;p&gt;index视图将显示所有的帖子，最近的一个。这里使用了&lt;code&gt;JOIN&lt;/code&gt;，可以关联user表来获得用户的信息在结果中展示。&lt;/p&gt;
&lt;p&gt;baby/blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; posts &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SELECT p.id, title, body, created, author_id, username&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; FROM post p JOIN user u ON p.author_id = u.id&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; ORDER BY created DESC&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchall()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;blog/index.html&amp;#39;&lt;/span&gt;, posts&lt;span style="color:#f92672"&gt;=&lt;/span&gt;posts)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;baby/templates/blog/index.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% extends &amp;#39;base.html&amp;#39; %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block header %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;{% block title %}Posts{% endblock %}&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% if g.user %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;action&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;blog.create&amp;#39;)}}&amp;#34;&lt;/span&gt;&amp;gt;New&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endif %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block content %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% for post in posts %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;article&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;{{ post[&amp;#39;title&amp;#39;] }}&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;about&amp;#34;&lt;/span&gt;&amp;gt;by {{ post[&amp;#39;username&amp;#39;] }} on {{ post[&amp;#39;created&amp;#39;].strftime(&amp;#39;%Y-%m-%d&amp;#39;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% if g.user[&amp;#39;id&amp;#39;] == post[&amp;#39;author_id&amp;#39;] %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;action&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{ url_for(&amp;#39;blog.update&amp;#39;, id=post[&amp;#39;id&amp;#39;]) }}&amp;#34;&lt;/span&gt;&amp;gt;Edit&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endif %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;p&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&amp;gt;{{ post[&amp;#39;body&amp;#39;] }}&amp;lt;/&lt;span style="color:#f92672"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;article&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% if not loop.last %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;hr&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endif %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endfor %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当用户登录后，&lt;code&gt;header&lt;/code&gt;部分会添加一个&lt;code&gt;create&lt;/code&gt;视图的链接，当用户是帖子的作者，将会看到一个指向&lt;code&gt;update&lt;/code&gt;视图的&amp;rsquo;Edit&amp;rsquo;链接。&lt;code&gt;loop.last&lt;/code&gt;是&lt;a class="link" href="http://jinja.pocoo.org/docs/2.10/templates/#for" target="_blank" rel="noopener"
 &gt;Jinja for loops&lt;/a&gt;中的一个可用的特殊变量。它用于在每个帖子之后显示除最后一个之外的一行，以便在视觉上将它们分开。&lt;/p&gt;
&lt;h2 id="create视图"&gt;Create视图
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;create&lt;/code&gt;视图与身份认证&lt;code&gt;register&lt;/code&gt;视图的工作方式相同。显示表单，或验证发布的数据，并将帖子添加到数据库或显示错误。之前编写的&lt;code&gt;login_required&lt;/code&gt;装饰器用于博客视图。一个用户必须在登录后才能访问这些视图，否则用户将被重定向到登录页面。&lt;/p&gt;
&lt;p&gt;baby/blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/create&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@login_required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; title:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Title is required&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; error &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flash(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;INSERT INTO post (title, body, author_id)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; VALUES (?,?,?)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (title, body, g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user[&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;blog.index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;blog/create.html&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;baby/templates/blog/create.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% extends &amp;#39;base.html&amp;#39; %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block header %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;{% block title %}New Posts{% endblock %}&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block content %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;form&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&amp;gt;Title&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{ request.form[&amp;#39;title&amp;#39;] }}&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;required&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&amp;gt;body&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;textarea&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{ request.form[&amp;#39;body&amp;#39;] }}&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;textarea&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Save&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="update视图"&gt;Update视图
&lt;/h2&gt;&lt;p&gt;update和delete视图需要通过id获取post，并且检查post的作者与登录用户是否匹配。为了避免代码重复，可以写一个函数来获取post，并且每个视图都能调用。&lt;/p&gt;
&lt;p&gt;baby/blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_post&lt;/span&gt;(id, check_author&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SELECT p.id, title, body, created, author_id, username&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; FROM post p JOIN user u ON p.author_id = u.id&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; WHERE p.id = ?&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (id,)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; post &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; abort(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Post id &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{0}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; doesn&amp;#39;t exist&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(id))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; check_author &lt;span style="color:#f92672"&gt;and&lt;/span&gt; post[&lt;span style="color:#e6db74"&gt;&amp;#39;author_id&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user[&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; abort(&lt;span style="color:#ae81ff"&gt;403&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; post
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.abort" target="_blank" rel="noopener"
 &gt;abort()&lt;/a&gt;将引发一个返回HTTP状态代码的特殊异常。它需要一个可选的消息来显示错误，否则使用默认消息。 &lt;code&gt;404&lt;/code&gt;意味着“Not Found”，&lt;code&gt;403&lt;/code&gt;意味着“Forbidden”（&lt;code&gt;401&lt;/code&gt;意味着“Unauthorized”，但是我们应该重定向到登录页面而不是返回该状态。）&lt;/p&gt;
&lt;p&gt;定义的&lt;code&gt;check_author&lt;/code&gt;参数，以便该函数可用于获取帖子而无需检查作者。如果您编写一个视图来显示页面上的单个帖子，用户无关紧要，因为他们没有修改帖子，这将非常有用。&lt;/p&gt;
&lt;p&gt;baby/blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;lt;int:id&amp;gt;/update&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@login_required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update&lt;/span&gt;(id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_post(id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; title:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Title is required&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; error &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flash(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;UPDATE post SET title = ?, body = ?&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; WHERE id = ?&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (title, body, id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;blog.index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;blog/update.html&amp;#39;&lt;/span&gt;, post&lt;span style="color:#f92672"&gt;=&lt;/span&gt;post)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个视图跟前面的视图不太一样，&lt;code&gt;update&lt;/code&gt;函数需要传递一个参数&lt;code&gt;id&lt;/code&gt;，这对应于路径中的&lt;code&gt;&amp;lt;int:id&amp;gt;&lt;/code&gt;。真实的URL看起来像这个&lt;code&gt;/1/update/&lt;/code&gt;，Flask将会捕捉到&lt;code&gt;1&lt;/code&gt;，并保证它是个int，并且传递它作为id参数。如果不是一个具体的&lt;code&gt;int:&lt;/code&gt;而是一个&lt;code&gt;&amp;lt;id&amp;gt;&lt;/code&gt;，获取到的参数将会是一个String。为了生成一个URL地址到update页面，&lt;code&gt;url_for&lt;/code&gt;需要传递一个&lt;code&gt;id&lt;/code&gt;参数，比如&lt;code&gt;url_for('blog.update', id=post['id'])&lt;/code&gt;。这个也可以在上面的&lt;code&gt;index.html&lt;/code&gt;中看到。&lt;/p&gt;
&lt;p&gt;create和update视图看起来很像，最大的不同是update视图使用一个post对象并且&lt;code&gt;UPDATE&lt;/code&gt;代替了&lt;code&gt;INSERT&lt;/code&gt;。通过一些巧妙的重构，你可以使用一个视图和模板替换掉create和update视图，但是由于是新手教程，保持两个视图分离看起来会比较清晰。&lt;/p&gt;
&lt;p&gt;baby/templates/blog/update.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% extends &amp;#39;base.html&amp;#39; %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block header %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;{% block title %}Edit &amp;#34;{{ post[&amp;#39;title&amp;#39;] }}&amp;#34;{% endblock %}&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block content %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;form&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&amp;gt;Title&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{ request.form[&amp;#39;title&amp;#39;] or post[&amp;#39;title&amp;#39;]}}&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;required&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&amp;gt;Body&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;textarea&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&amp;gt;{{ request.form[&amp;#39;body&amp;#39;] or post[&amp;#39;body&amp;#39;] }}&amp;lt;/&lt;span style="color:#f92672"&gt;textarea&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Save&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;hr&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;form&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;action&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;blog.delete&amp;#39;, id=post[&amp;#39;id&amp;#39;])}}&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;danger&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Delete&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onclick&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;return confirm(&amp;#39;Are you sure?&amp;#39;);&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;{{ request.form['title'] or post['title'] }}&lt;/code&gt;用来选择表单中显示的数据，当表单没有被提交时，原始的post的数据将会显示，但是如果无效的表单数据被提交，我们希望显示这个错误的数据，并让用户去修改此错误，因此使用&lt;code&gt;request.form['title']&lt;/code&gt;。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request&lt;/strong&gt;&lt;/a&gt;是另一个变量，可以自动在模板中使用。&lt;/p&gt;
&lt;h2 id="delete视图"&gt;Delete视图
&lt;/h2&gt;&lt;p&gt;delete视图没有自己的模板，delete按钮是&lt;code&gt;update.html&lt;/code&gt;s的一部分并发布到&lt;code&gt;/&amp;lt;id&amp;gt;/delete&lt;/code&gt;网址。因此没有模板，它只是处理&lt;code&gt;POST&lt;/code&gt;方法并且重定向到&lt;code&gt;index&lt;/code&gt;视图。&lt;/p&gt;
&lt;p&gt;baby/blog.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;lt;int:id&amp;gt;/delete&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@login_required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delete&lt;/span&gt;(id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; get_post(id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;DELETE FROM post where id = ?&amp;#39;&lt;/span&gt;, (id,)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;blog.index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到这里我们就已经完成了如何开发一个应用程序的新手教程，可喜可贺，花一些时间在浏览器上试试所有的功能。然而到这里在项目完成之前还是有很多要做的。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/159" target="_blank" rel="noopener"
 &gt;使项目可安装&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 静态文件</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-jing-tai-wen-jian/</link><pubDate>Tue, 29 Jul 2025 10:09:47 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-jing-tai-wen-jian/</guid><description>&lt;p&gt;身份验证视图和模板可以正常工作，但它们现在看起来很简单。可以添加一些&lt;a class="link" href="https://developer.mozilla.org/docs/Web/CSS" target="_blank" rel="noopener"
 &gt;CSS&lt;/a&gt;来为您构建的HTML布局添加样式。样式不会改变，因此它是&lt;em&gt;静态&lt;/em&gt;文件而不是模板。&lt;/p&gt;
&lt;p&gt;Flask会自动添加一个&lt;code&gt;static&lt;/code&gt;视图，该视图采用相对于&lt;code&gt;baby/static&lt;/code&gt;目录的路径并为其提供服务。&lt;code&gt;base.html&lt;/code&gt;模板已经有一个指向&lt;code&gt;app.css&lt;/code&gt;文件的链接&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;link&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rel&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;app.css&amp;#39;)}}&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;除了CSS之外，其他类型的静态文件可能是具有JavaScript功能的文件或logo图像。它们都放在&lt;code&gt;baby/static&lt;/code&gt;目录下，并用&lt;code&gt;url_for('static'，filename ='...')&lt;/code&gt;引用。&lt;/p&gt;
&lt;p&gt;这个教程的目标不在于教你如何写CSS，因此你只需要将下面的代码复制到&lt;code&gt;baby/static/app.css&lt;/code&gt;文件中即可。&lt;/p&gt;
&lt;p&gt;baby/static/app.css&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;font-family&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;sans-serif&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;background&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;#eee&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;max-width&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;960&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;px&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;background&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;white&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;font-family&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;serif&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;color&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;#377ba8&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;color&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;#377ba8&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;hr&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;border&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;none&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;border-top&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;px&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;solid&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;lightgray&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;nav&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;background&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;lightgray&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;display&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;align-items&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;center&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;nav&lt;/span&gt; &lt;span style="color:#f92672"&gt;h1&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;nav&lt;/span&gt; &lt;span style="color:#f92672"&gt;h1&lt;/span&gt; &lt;span style="color:#f92672"&gt;a&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;text-decoration&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;none&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.25&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;nav&lt;/span&gt; &lt;span style="color:#f92672"&gt;ul&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;display&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;list-style&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;none&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;nav&lt;/span&gt; &lt;span style="color:#f92672"&gt;ul&lt;/span&gt; &lt;span style="color:#f92672"&gt;li&lt;/span&gt; &lt;span style="color:#f92672"&gt;a&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#f92672"&gt;nav&lt;/span&gt; &lt;span style="color:#f92672"&gt;ul&lt;/span&gt; &lt;span style="color:#f92672"&gt;li&lt;/span&gt; &lt;span style="color:#f92672"&gt;span&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#f92672"&gt;header&lt;/span&gt; .&lt;span style="color:#a6e22e"&gt;action&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;display&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;block&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;header&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;border-bottom&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;px&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;solid&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;lightgray&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;display&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;align-items&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex-end&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;header&lt;/span&gt; &lt;span style="color:#f92672"&gt;h1&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.25&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;rem&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;flash&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;background&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;#cae6f6&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;border&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;px&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;solid&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;#377ba8&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;header&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;display&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;align-items&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex-end&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;font-size&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.85&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;header&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;div&lt;/span&gt;:&lt;span style="color:#a6e22e"&gt;first-of-type&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;header&lt;/span&gt; &lt;span style="color:#f92672"&gt;h1&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;font-size&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1.5&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin-bottom&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt; .&lt;span style="color:#a6e22e"&gt;about&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;color&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;slategray&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;font-style&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;italic&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt; .&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;white-space&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;pre-line&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;:&lt;span style="color:#a6e22e"&gt;last-child&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;margin-bottom&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;form&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;margin&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;display&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;flex&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;flex-direction&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;column&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;label&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;font-weight&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;bold&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;margin-bottom&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; .&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;textarea&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;margin-bottom&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt; &lt;span style="color:#f92672"&gt;textarea&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;min-height&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;resize&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;vertical&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;danger&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;color&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;#cc2f2e&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#f92672"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#f92672"&gt;submit&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;align-self&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;start&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;min-width&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;您可以在&lt;a class="link" href="https://github.com/pallets/flask/tree/1.0.2/examples/tutorial/flaskr/static/style.css" target="_blank" rel="noopener"
 &gt;示例代码中&lt;/a&gt;找到一个不太紧凑的&lt;code&gt;style.css&lt;/code&gt;版本。&lt;/p&gt;
&lt;p&gt;转到&lt;a class="link" href="http://127.0.0.1:5000/auth/login%EF%BC%8C%E8%AF%A5%E9%A1%B5%E9%9D%A2%E5%BA%94%E5%A6%82%E4%B8%8B%E9%9D%A2%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%E6%89%80%E7%A4%BA%E3%80%82" target="_blank" rel="noopener"
 &gt;http://127.0.0.1:5000/auth/login&lt;/a&gt;，该页面应如下面的屏幕截图所示。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://camo.githubusercontent.com/e31114c998d002420ce5ee63b5640d1b1ee96d87/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433333039302f77616c6b6572667265652f77785f3135315f322e706e67" target="_blank" rel="noopener"
 &gt;&lt;img alt="静态文件" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://camo.githubusercontent.com/e31114c998d002420ce5ee63b5640d1b1ee96d87/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433333039302f77616c6b6572667265652f77785f3135315f322e706e67"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;您可以从&lt;a class="link" href="https://developer.mozilla.org/docs/Web/CSS" target="_blank" rel="noopener"
 &gt;Mozilla的文档&lt;/a&gt;中了解有关CSS的更多信息。如果更改静态文件，请刷新浏览器页面。如果更改未显示，请尝试清除浏览器的缓存。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/158" target="_blank" rel="noopener"
 &gt;博客Blueprint&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 模板</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-mu-ban/</link><pubDate>Tue, 29 Jul 2025 10:09:44 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-mu-ban/</guid><description>&lt;p&gt;经过上篇文章，我们已经为应用程序编写了身份验证视图，当时当我们运行服务并且试着去访问其中任何一个URL的时候，将会看到&lt;code&gt;TemplateNotFound&lt;/code&gt;的错误。那是因为视图调用了&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.render_template" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;render_template()&lt;/strong&gt;&lt;/a&gt;函数，但是我们还没有创建对应的模板。模板文件将被存储在&lt;code&gt;baby&lt;/code&gt;包中的&lt;code&gt;templates&lt;/code&gt;文件夹中。&lt;/p&gt;
&lt;p&gt;模板是包含静态数据和动态数据占位符的文件。一个模板使用特定数据呈现以生成最终文档。Flask使用&lt;a class="link" href="http://jinja.pocoo.org/docs/templates/" target="_blank" rel="noopener"
 &gt;Jinja&lt;/a&gt;模板库来渲染模板。&lt;/p&gt;
&lt;p&gt;在应用程序中，将使用templates去渲染将在用户浏览器上显示的&lt;a class="link" href="https://developer.mozilla.org/docs/Web/HTML" target="_blank" rel="noopener"
 &gt;HTML&lt;/a&gt;，在Flask中，Jinja被配置为自动转移将在HTML模板中渲染的任何数据。这意味着用户的输入将被安全的渲染，他们输入的任何字符都可能破坏HTML。例如&lt;code&gt;&amp;lt;&lt;/code&gt;和&lt;code&gt;&amp;gt;&lt;/code&gt;将转义为安全值，这些值在浏览器中看起来相同，但不会造成不必要的影响。&lt;/p&gt;
&lt;p&gt;Jinja的外观和行为都很像Python。使用特殊的分隔符将Jinja语法与模板中的静态数据区分开来，&lt;code&gt;{{&lt;/code&gt;和&lt;code&gt;}}&lt;/code&gt;的表达式符号表示他们之间的任何内容都是将输出到最终文档。&lt;code&gt;{%&lt;/code&gt;和&lt;code&gt;%}&lt;/code&gt;符号表示类似if和for的控制流语句。不像Python，块由开始和结束标记表示，而不是缩进，因为块内的静态文本可以更改缩进。&lt;/p&gt;
&lt;h2 id="基础布局"&gt;基础布局
&lt;/h2&gt;&lt;p&gt;在应用程序中每个页面都有一个类似的基础布局包裹着不同的body。而不是在每个模板中编写整个HTML结构，每个模板将会继承基础布局然后重写具体的部分。&lt;/p&gt;
&lt;p&gt;baby/templates/base.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;{% block title %}{% endblock %} - Baby&amp;lt;/&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;link&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rel&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;app.css&amp;#39;)}}&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;nav&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;Baby&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;ul&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% if g.user %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style="color:#f92672"&gt;span&lt;/span&gt;&amp;gt;{{g.user[&amp;#39;username&amp;#39;]}}&amp;lt;/&lt;span style="color:#f92672"&gt;span&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;auth.logout&amp;#39;)}}&amp;#34;&lt;/span&gt;&amp;gt;Log Out&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% else %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;auth.register&amp;#39;)}}&amp;#34;&lt;/span&gt;&amp;gt;Register&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{url_for(&amp;#39;auth.login&amp;#39;)}}&amp;#34;&lt;/span&gt;&amp;gt;Log In&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endif %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;ul&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;nav&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;section&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% block header %}{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;header&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% for message in get_flashed_messages() %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;flash&amp;#34;&lt;/span&gt;&amp;gt;{{message}}&amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% endfor %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% block content %}{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;section&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;g&lt;/strong&gt;&lt;/a&gt;在模板中将会自动可用，基于&lt;code&gt;g.user&lt;/code&gt;设置用户(从&lt;code&gt;load_logged_in_user&lt;/code&gt;)，要么显示用户名和注销链接，要么显示注册和登录链接。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;url_for()&lt;/strong&gt;&lt;/a&gt;也将会自动可用，并且被用于生成URL，而不是手动将他们写出来。&lt;/p&gt;
&lt;p&gt;在页面title的后面，并且在content的前面，模板循环调用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.get_flashed_messages" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;get_flashed_messages()&lt;/strong&gt;&lt;/a&gt;返回的每个信息。您在视图中使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.flash" target="_blank" rel="noopener"
 &gt;flash()&lt;/a&gt;来显示错误消息，下面的代码将显示它们。&lt;/p&gt;
&lt;p&gt;这里的有三个被定义的块，在其他模板中将被重写。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;{% block title %}&lt;/code&gt;将改变在浏览器标签显示的标题和窗口的标题&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{% block header %}&lt;/code&gt;类似于&lt;code&gt;title&lt;/code&gt;，但是会改变在页面上显示的标题&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{% block content %}&lt;/code&gt;是每个页面的内容所在，例如登录表单或博客文章&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;基础模板直接在模板的目录下面。为了保持其他模板的组织，蓝图的模板将被放置在与蓝图同名的目录中&lt;/p&gt;
&lt;h2 id="注册"&gt;注册
&lt;/h2&gt;&lt;p&gt;baby/templates/auth/register.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% extends &amp;#39;base.html&amp;#39; %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block header %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;{% block title %}Register{% endblock %}&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block content %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;form&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&amp;gt;Username&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;required&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&amp;gt;Password&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;required&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Register&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;{% extends 'base.html' %}&lt;/code&gt;告诉Jinja这个模板应该替换掉基础模板中的block。所有渲染的内容都必须出现在&lt;code&gt;{% block %}&lt;/code&gt;标记中，这些标记覆盖基础模板中的块。&lt;/p&gt;
&lt;p&gt;这里使用的一个有用模式是将&lt;code&gt;{% block title %}&lt;/code&gt;放在&lt;code&gt;{% block header %}&lt;/code&gt;中。这将设置title块，然后将其值输出到header块中，这样窗口和页面就可以共享相同的标题，而不需要编写两次标题。&lt;/p&gt;
&lt;p&gt;input标签有一个required属性，这是在告诉浏览器知道所有的输入框都填入数据才能被提交。如果用户使用的是老版本的浏览器并且不支持这个属性，或者使用了除了浏览器之外其他的工具做了请求操作，你仍然应该在视图中去验证数据。始终在服务器上完全验证数据是很重要的，即使客户机也执行一些验证。&lt;/p&gt;
&lt;h2 id="登录"&gt;登录
&lt;/h2&gt;&lt;p&gt;这与注册模板相同，除了标题和提交按钮。&lt;/p&gt;
&lt;p&gt;baby/templates/auth/login.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% extends &amp;#34;base.html&amp;#34; %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block header %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;{% block title %}Login In{% endblock %}&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% block content %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;form&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&amp;gt;Username&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;required&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;label&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&amp;gt;Password&amp;lt;/&lt;span style="color:#f92672"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;required&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Login In&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="注册一个用户"&gt;注册一个用户
&lt;/h2&gt;&lt;p&gt;现在身份认证模块已经写完，你可以注册一个用户。确保服务器仍在运行(如果没有运行，执行&lt;code&gt;flask run&lt;/code&gt;)，然后访问&lt;code&gt;http://127.0.0.1:5000/auth/register&lt;/code&gt;。 试着在没有填写任何内容的情况下点击“注册”按钮，然后应该可以看到浏览器的错误提示信息。试着从&lt;code&gt;register.html&lt;/code&gt;模板中将&lt;code&gt;required&lt;/code&gt;属性移除，然后点击再次点击“注册”按钮。与浏览器显示错误不同，页面将重新加载，视图中的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.flash" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;flash()&lt;/strong&gt;&lt;/a&gt;将显示错误信息。&lt;/p&gt;
&lt;p&gt;填充一个username和password，然后你将被重定向到一个登录页面，试着输入一个错误的username或者是正确的username和错误的password。如果你登录进去后，你将会得到一个错误因为还没有一个&lt;code&gt;index&lt;/code&gt;视图去跳转。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/157" target="_blank" rel="noopener"
 &gt;静态文件&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - Blueprint和视图</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-blueprint-he-shi-tu/</link><pubDate>Tue, 29 Jul 2025 10:09:41 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-blueprint-he-shi-tu/</guid><description>&lt;p&gt;视图函数是我们编写用于响应对应的应用程序的请求的代码。Flask使用模式将传入的请求URL与应该处理它的视图相匹配。该视图返回Flask转换后的响应数据。Flask也可以转向另一个方向，并根据其名称和参数生成视图的URL。&lt;/p&gt;
&lt;h2 id="创建blueprint"&gt;创建Blueprint
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Blueprint" target="_blank" rel="noopener"
 &gt;Blueprint&lt;/a&gt;是一种组织一组相关视图和其他代码的方法。他们不是直接在应用程序中注册视图和其他代码，而是使用蓝图进行注册。然后，当它在工厂功能中可用时，蓝图就会在应用程序中注册。&lt;/p&gt;
&lt;p&gt;baby将会有两个Blueprint，一个用于身份验证功能，另一个用于博客帖子功能。每个蓝图的代码都将在一个单独的模块中。由于博客需要了解身份验证，因此首先要编写身份验证的部分。&lt;/p&gt;
&lt;p&gt;baby/auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; functools
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Blueprint, flash, g, redirect, render_template, request, session, url_for
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.security &lt;span style="color:#f92672"&gt;import&lt;/span&gt; check_password_hash, generate_password_hash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; baby.db &lt;span style="color:#f92672"&gt;import&lt;/span&gt; get_db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Blueprint(&lt;span style="color:#e6db74"&gt;&amp;#39;auth&amp;#39;&lt;/span&gt;, __name__, url_prefix&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;/auth&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的代码创建了一个名为&amp;rsquo;auth&amp;rsquo;的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Blueprint" target="_blank" rel="noopener"
 &gt;Blueprint&lt;/a&gt;，像应用程序对象一样，blueprint需要知道它是在哪里定义的，因此传递一个&lt;code&gt;__name__&lt;/code&gt;作为第二个参数。&lt;code&gt;url_prefix&lt;/code&gt;的值将被追加到所有与blueprint有关的URLs的前面。&lt;/p&gt;
&lt;p&gt;在工厂函数中，使用app.register_blueprint()导入并注册blueprint。放置新的代码在工厂函数末尾，在返回app之前。&lt;/p&gt;
&lt;p&gt;baby/&lt;strong&gt;init&lt;/strong&gt;.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;///&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 忽略已存在的代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; . &lt;span style="color:#f92672"&gt;import&lt;/span&gt; auth
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register_blueprint(auth&lt;span style="color:#f92672"&gt;.&lt;/span&gt;bp)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;身份认证功能的blueprint会有几个试图，注册新用户的视图、登录的视图、登出的视图。&lt;/p&gt;
&lt;h2 id="第一个视图注册"&gt;第一个视图：注册
&lt;/h2&gt;&lt;p&gt;当用户访问&lt;code&gt;/auth/register&lt;/code&gt;的URL时，&lt;code&gt;register&lt;/code&gt;视图将返回一个&lt;a class="link" href="https://developer.mozilla.org/docs/Web/HTML" target="_blank" rel="noopener"
 &gt;HTML&lt;/a&gt;表单以供用户填写。当用户提交表单时，视图函数将会验证用户的输入，如果验证失败将会显示一个带有错误信息的表单，如果验证成功则创建一个新用户，并进入到登录页面。&lt;/p&gt;
&lt;p&gt;现在我们先写一部分视图代码，在下一篇分享中，将介绍如何用模板去生成一个HTML表单。&lt;/p&gt;
&lt;p&gt;baby/auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/register&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;register&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; username:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Username is required.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;elif&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; password:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Password is required.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;elif&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SELECT id FROM user WHERE username = ?&amp;#39;&lt;/span&gt;, (username,)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone() &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;user &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; is already registered.&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(username)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; error &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;INSERT INTO user (username, password) VALUES (?, ?)&amp;#39;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (username, generate_password_hash(password))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;auth.login&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flash(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;auth/register.html&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面解说下这个&lt;code&gt;register&lt;/code&gt;视图函数正在做的&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Blueprint.route" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;@bp.route()&lt;/strong&gt;&lt;/a&gt;将URL&lt;code&gt;/register&lt;/code&gt;与&lt;code&gt;register&lt;/code&gt;视图函数相关联。当Flask收到对&lt;code&gt;/auth/register&lt;/code&gt;的请求时，它将调用&lt;code&gt;register&lt;/code&gt;视图并使用返回值作为响应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果用户提交了表单，&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Request.method" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request.method&lt;/strong&gt;&lt;/a&gt;将是&lt;code&gt;'POST'&lt;/code&gt;，在这种情况下，请开始验证输入&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Request.form" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;request.form&lt;/strong&gt;&lt;/a&gt;是一种特殊类型的&lt;a class="link" href="https://docs.python.org/3/library/stdtypes.html#dict" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;dict&lt;/strong&gt;&lt;/a&gt;映射，提交了表单键和值。用户将输入他们的&lt;code&gt;username&lt;/code&gt;和&lt;code&gt;password&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;验证&lt;code&gt;username&lt;/code&gt;和&lt;code&gt;password&lt;/code&gt;不为空。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过查询数据库并通过返回的结果验证&lt;code&gt;username&lt;/code&gt;是否已经被注册过了。&lt;a class="link" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.execute" target="_blank" rel="noopener"
 &gt;db.execute&lt;/a&gt;采用SQL查询&lt;code&gt;?&lt;/code&gt;任何用户输入的占位符，以及用于替换占位符的值元组。数据库库将负责转义值，因此您不容易受到&lt;em&gt;SQL注入攻击&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchone" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;fetchone()&lt;/strong&gt;&lt;/a&gt;从查询中返回一行。如果查询没有返回结果，它会返回None。稍后，使用&lt;a class="link" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchall" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;fetchall()&lt;/strong&gt;&lt;/a&gt;，它返回所有结果的列表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果验证成功了则插入新用户的数据到数据库。为了安考虑，密码永远都不要用明文还自己存储，而是使用&lt;a class="link" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.security.generate_password_hash" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;generate_password_hash()&lt;/strong&gt;&lt;/a&gt;安全地将明文密码机型哈希处理，然后存储哈希值。由于此查询修改数据，因此需要在之后调用&lt;a class="link" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.commit" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;db.commit()&lt;/strong&gt;&lt;/a&gt;以保存更改。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;存储用户之后，被重定向到登录页面。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;url_for()&lt;/a&gt;根据其名称生成一个登录视图URL。这比直接编写URL更好，因为它允许您稍后更改URL而不更改链接到它的所有代码。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.redirect" target="_blank" rel="noopener"
 &gt;redirect()&lt;/a&gt;生成对生成的URL的重定向响应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果验证失败，错误信息将被显示给用户。flash()存储的信息，在模板渲染的时候能被检索出来。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当用户最初导航到&lt;code&gt;auth/register&lt;/code&gt;，或者存在验证错误时，应显示带有注册表单的HTML页面。&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.render_template" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;render_template()&lt;/strong&gt;&lt;/a&gt;将呈现包含HTML的模板，您将在本教程的下一步中编写该模板。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="第二个视图登录"&gt;第二个视图：登录
&lt;/h2&gt;&lt;p&gt;该视图遵循与上面的&lt;code&gt;register&lt;/code&gt;视图相同的模式。&lt;/p&gt;
&lt;p&gt;baby/auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM user WHERE username = ?&amp;#39;&lt;/span&gt;, (username,)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; user &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect username.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;elif&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; check_password_hash(user[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;], password):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Incorrect password.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; error &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;clear()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session[&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user[&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;blog.index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flash(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;auth/login.html&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个视图与&lt;code&gt;register&lt;/code&gt;视图有一些不同之处：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先查询用户并将其存储在变量中以供以后使用。&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.security.check_password_hash" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;check_password_hash()&lt;/strong&gt;&lt;/a&gt;以存储的哈希相同的方式哈希处理提交的密码并安全地比较它们。如果匹配，则密码有效。&lt;/li&gt;
&lt;li&gt;session是一个跨请求存储数据的字典。验证成功后，用户的&lt;code&gt;id&lt;/code&gt;将存储在新会话中。数据存储在发送到浏览器的&lt;em&gt;cookie&lt;/em&gt;中，然后浏览器将其与后续请求一起发回。Flask安全地对数据进行签名，以便不会被篡改。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;现在用户的id存储在会话中，它将在后续请求中可用。在每个请求开始时，如果用户已登录，则应加载其信息并使其可供其他视图使用。&lt;/p&gt;
&lt;p&gt;baby/auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.before_app_request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;load_logged_in_user&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; user_id &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM user WHERE id = ?&amp;#39;&lt;/span&gt;, (user_id,)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fetchone()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Blueprint.before_app_request" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;bp.before_app_request()&lt;/strong&gt;&lt;/a&gt;注册一个在视图函数之前运行的函数，无论请求什么URL。&lt;code&gt;load_logged_in_user&lt;/code&gt;检查用户id是否存储在&lt;code&gt;session&lt;/code&gt;中并从数据库中获取该用户的数据，并将其存储在&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.g" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;g.user&lt;/strong&gt;&lt;/a&gt;上，该持续时间为请求的长度。如果没有用户ID，或者id不存在，则&lt;code&gt;g.user&lt;/code&gt;将为&lt;code&gt;None&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="第三个视图登出退出"&gt;第三个视图：登出/退出
&lt;/h2&gt;&lt;p&gt;要注销，需要从会话中删除用户ID。然后load_logged_in_user将不会在后续请求中加载用户。&lt;/p&gt;
&lt;p&gt;baby/auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@bp.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/logout&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;logout&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;clear()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;blog.index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="在其他视图中需要身份验证"&gt;在其他视图中需要身份验证
&lt;/h2&gt;&lt;p&gt;创建，编辑和删除博客帖子将需要用户登录。装饰器可用于检查它应用于的每个视图。&lt;/p&gt;
&lt;p&gt;baby/auth.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login_required&lt;/span&gt;(view):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@functools.wraps&lt;/span&gt;(view)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;wrapped_view&lt;/span&gt;(&lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;auth.login&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; view(&lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; wrapped_view
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个装饰器返回一个新的视图函数，它包装了它应用的原始视图。新函数检查用户是否已加载，未加载的话将重定向到登录页面。如果加载了用户，则调用原始视图并继续正常运行。在编写博客视图时，您将使用此装饰器。&lt;/p&gt;
&lt;h2 id="endpoint和url"&gt;Endpoint和URL
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;url_for()&lt;/a&gt;函数根据名称和参数生成视图的URL。与视图关联的名称也称为endpoint，默认情况下，它与视图函数的名称相同。&lt;/p&gt;
&lt;p&gt;例如，在本教程前面添加到app工厂的&lt;code&gt;hello()&lt;/code&gt;视图名为&lt;code&gt;'hello'&lt;/code&gt;，可以链接到&lt;code&gt;url_for('hello')&lt;/code&gt;。如果这个视图函数带了一个参数，这部分将在后面的教程中看到，他将被连接到&lt;code&gt;url_for('hello', who='World')&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;使用blueprint时，blueprint的名称将附加到函数的名称前面，因此上面写入的登录函数的endpoint是&lt;code&gt;'auth.login'&lt;/code&gt;，因为我们将其添加到&lt;code&gt;'auth'&lt;/code&gt; blueprint中。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/156" target="_blank" rel="noopener"
 &gt;模板&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 定义和访问数据库</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-ding-yi-he-fang-wen-shu-ju-ku/</link><pubDate>Tue, 29 Jul 2025 10:09:38 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-ding-yi-he-fang-wen-shu-ju-ku/</guid><description>&lt;p&gt;本教程的应用程序将使用SQLite数据库来存储用户和帖子，Python在sqlite3模块中内置了对SQLite的支持&lt;/p&gt;
&lt;p&gt;SQLite是很方便的，因为他不需要设置单独的数据库服务器并且内置与Python中。然而如果并发请求同时试着写入数据，随着每次写入顺序发生，它们会变慢。&lt;/p&gt;
&lt;p&gt;小应用程序不会注意到这一点。一旦变大，您可能希望切换到其他数据库。&lt;/p&gt;
&lt;p&gt;本教程不会详细介绍SQL。如果您不熟悉它，SQLite文档会描述该&lt;a class="link" href="https://sqlite.org/lang.html" target="_blank" rel="noopener"
 &gt;语言&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="连接数据库"&gt;连接数据库
&lt;/h2&gt;&lt;p&gt;使用SQLite数据库（以及大多数其他Python数据库）时要做的第一件事就是创建一个连接。使用连接执行任何查询和操作，该连接在工作完成后关闭。&lt;/p&gt;
&lt;p&gt;在Web应用程序中，此连接通常与请求相关联。它在处理请求时在某个时刻创建，并在发送响应之前关闭。&lt;/p&gt;
&lt;p&gt;baby/db.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; sqlite3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; click
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; current_app, g
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.cli &lt;span style="color:#f92672"&gt;import&lt;/span&gt; with_appcontext
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_db&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; g:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sqlite3&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;DATABASE&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; detect_types&lt;span style="color:#f92672"&gt;=&lt;/span&gt;sqlite3&lt;span style="color:#f92672"&gt;.&lt;/span&gt;PARSE_DECLTYPES
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;row_factory &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sqlite3&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Row
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;close_db&lt;/span&gt;(e&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pop(&lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; db &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.g" target="_blank" rel="noopener"
 &gt;g&lt;/a&gt;是一个特殊对象，对每个请求都是唯一的。它用于存储请求期间可能由多个函数访问的数据。&lt;/p&gt;
&lt;p&gt;如果在同一请求中第二次调用&lt;code&gt;get_db&lt;/code&gt;，则会存储并重用连接，而不是创建新连接。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;current_app&lt;/a&gt;是另一个指向处理请求的Flask应用程序的特殊对象。由于您使用的是应用程序工厂，因此在编写其余代码时没有应用程序对象。创建应用程序并处理请求时将调用&lt;code&gt;get_db&lt;/code&gt;，因此可以使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.current_app" target="_blank" rel="noopener"
 &gt;current_app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.connect" target="_blank" rel="noopener"
 &gt;sqlite3.connect()&lt;/a&gt;建立与DATABASE配置键指向的文件的连接。此文件尚不存在，并且在您稍后初始化数据库之前不会存在。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://docs.python.org/3/library/sqlite3.html#sqlite3.Row" target="_blank" rel="noopener"
 &gt;sqlite3.Row&lt;/a&gt;告诉连接返回行为类似于dicts的行。这允许按名称访问列。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;close_db&lt;/code&gt;通过检查是否已设置&lt;code&gt;g.db&lt;/code&gt;来检查是否创建了连接。如果连接存在，则关闭。再往下，您将告诉您的应用程序有关应用程序工厂中的&lt;code&gt;close_db&lt;/code&gt;函数，以便在每次请求后调用它。&lt;/p&gt;
&lt;h2 id="建表"&gt;建表
&lt;/h2&gt;&lt;p&gt;在SQLite中，数据存储在&lt;em&gt;表&lt;/em&gt;和&lt;em&gt;列&lt;/em&gt;中。这些需要在存储和检索数据之前创建。Baby项目将用户存储在用户表中，并在post表中存储帖子。使用创建空表所需的SQL命令创建一个文件：&lt;/p&gt;
&lt;p&gt;baby/schema.sql&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;DROP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;user&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;DROP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; post;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;user&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id INTEGER &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt; AUTOINCREMENT,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username TEXT &lt;span style="color:#66d9ef"&gt;UNIQUE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; post (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id INTEGER &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt; AUTOINCREMENT,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author_id INTEGER &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; created &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CURRENT_TIMESTAMP&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FOREIGN&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt; (author_id) &lt;span style="color:#66d9ef"&gt;REFERENCES&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;user&lt;/span&gt; (id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将运行这些SQL命令的Python函数添加到db.py文件中：&lt;/p&gt;
&lt;p&gt;baby/db.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;init_db&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; get_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; current_app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;open_resource(&lt;span style="color:#e6db74"&gt;&amp;#39;schema.sql&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;executescript(f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;decode(&lt;span style="color:#e6db74"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@click.command&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;init-db&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@with_appcontext&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;init_db_command&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Clear the existing data and create new tables&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; init_db()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; click&lt;span style="color:#f92672"&gt;.&lt;/span&gt;echo(&lt;span style="color:#e6db74"&gt;&amp;#39;Initialized the database.&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.open_resource" target="_blank" rel="noopener"
 &gt;open_resource()&lt;/a&gt;打开一个相对于&lt;code&gt;baby&lt;/code&gt;包的文件，这很有用，因为在以后部署应用程序时你不一定知道该位置在哪里。&lt;code&gt;get_db&lt;/code&gt;返回数据库连接，用于执行从文件读取的命令。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://click.pocoo.org/api/#click.command" target="_blank" rel="noopener"
 &gt;click.command()&lt;/a&gt;定义了一个名为&lt;code&gt;init-db&lt;/code&gt;的命令行命令，该命令调用&lt;code&gt;init_db&lt;/code&gt;函数并向用户显示成功消息。您可以&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/cli/#cli" target="_blank" rel="noopener"
 &gt;阅读命令行界面&lt;/a&gt;以了解有关编写命令的更多信息。&lt;/p&gt;
&lt;h2 id="注册应用程序"&gt;注册应用程序
&lt;/h2&gt;&lt;p&gt;close_db和init_db_command函数需要在应用程序中被注册，否则他们将不会被应用程序调用。但是，由于您使用的是工厂函数，因此在编写函数时该实例不可用。相反，编写一个接受应用程序并进行注册的函数。&lt;/p&gt;
&lt;p&gt;baby/db.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;init_app&lt;/span&gt;(app):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;teardown_appcontext(close_db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;cli&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_command(init_db_command)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.teardown_appcontext" target="_blank" rel="noopener"
 &gt;app.teardown_appcontext()&lt;/a&gt;告诉Flask在返回响应后清理时调用该函数。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://click.pocoo.org/api/#click.Group.add_command" target="_blank" rel="noopener"
 &gt;app.cli.add_command()&lt;/a&gt;添加了一个可以使用flask命令调用的新命令。&lt;/p&gt;
&lt;p&gt;从工厂导入并调用此功能。在返回应用程序之前，将新代码放在工厂函数的末尾。&lt;/p&gt;
&lt;p&gt;baby/&lt;strong&gt;init&lt;/strong&gt;.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;(test_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# create and configure the app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__, instance_relative_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_mapping(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SECRET_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;dev&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DATABASE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;instance_path, &lt;span style="color:#e6db74"&gt;&amp;#39;baby.sqlite&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; test_config &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_pyfile(&lt;span style="color:#e6db74"&gt;&amp;#39;config.py&amp;#39;&lt;/span&gt;, silent&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_mapping(test_config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;makedirs(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;instance_path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OSError&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/baby&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Baby&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 新加&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; . &lt;span style="color:#f92672"&gt;import&lt;/span&gt; db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="初始化数据库文件"&gt;初始化数据库文件
&lt;/h2&gt;&lt;p&gt;现在已经在应用程序中注册了&lt;code&gt;init-db&lt;/code&gt;，可以使用&lt;code&gt;flask&lt;/code&gt;命令调用它，类似于上一节的&lt;code&gt;run&lt;/code&gt;命令。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="注意"&gt;注意：
&lt;/h3&gt;&lt;p&gt;如果您仍在从上一页运行服务器，则可以停止服务器，也可以在新终端中运行此命令。如果您使用新终端，请记住更改到项目目录并激活env，如&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/installation/#install-activate-env" target="_blank" rel="noopener"
 &gt;激活环境&lt;/a&gt;中所述。您还需要设置FLASK_APP和FLASK_ENV，如上一节所示。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;运行&lt;code&gt;init-db&lt;/code&gt;命令&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask init-db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Initialized the database
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在，项目中的实例文件夹中将有一个baby.sqlite文件。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/155" target="_blank" rel="noopener"
 &gt;Blueprint和视图&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 应用程序设置</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-ying-yong-cheng-xu-she-zhi/</link><pubDate>Tue, 29 Jul 2025 10:09:35 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-ying-yong-cheng-xu-she-zhi/</guid><description>&lt;p&gt;Flask应用程序是一个&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;Flask&lt;/a&gt;类的实例。关于应用的任何事情，例如配置和URLs都将通过这个类来注册。&lt;/p&gt;
&lt;p&gt;最直截了当的方式创建一个Flask应用是直接在代码顶部通过创建一个全局的Flask实例，就像上一篇教程的“Hello，World！”示例一样。虽然这在某些情况下很简单且有用，但随着项目的增长，它可能会导致一些棘手的问题。&lt;/p&gt;
&lt;p&gt;在函数内创建Flask实例，而不是全局创建&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;Flask&lt;/a&gt;实例。此功能称为&lt;em&gt;应用程序工厂&lt;/em&gt;。应用程序需要的任何配置，注册和其他设置都将在函数内部进行，然后将返回应用程序。&lt;/p&gt;
&lt;h2 id="应用程序工厂"&gt;应用程序工厂
&lt;/h2&gt;&lt;p&gt;是时候开始编码了！创建&lt;code&gt;baby&lt;/code&gt;目录并添加&lt;code&gt;__init__.py&lt;/code&gt;文件。&lt;code&gt;__init__.py&lt;/code&gt;提供双重任务：它将包含应用工厂，并且它将告诉Python，&lt;code&gt;baby&lt;/code&gt;目录将被作为一个包来对待。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;baby/&lt;strong&gt;init&lt;/strong&gt;.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! _*_ coding: utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;(test_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 创建和配置应用程序&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__, instance_relative_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_mapping(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SECRET_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;dev&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DATABASE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;instance_path, &lt;span style="color:#e6db74"&gt;&amp;#39;baby.sqlite&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; test_config &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 加载实例配置（如果存在），不测试时&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_pyfile(&lt;span style="color:#e6db74"&gt;&amp;#39;config.py&amp;#39;&lt;/span&gt;, silent&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 如果传入则加载测试配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_mapping(test_config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 确保实例文件夹存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; os&lt;span style="color:#f92672"&gt;.&lt;/span&gt;makedirs(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;instance_path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OSError&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 一个简单的页面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/hello&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;application &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_app()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;create_app&lt;/code&gt;是应用程序工厂函数。您将在本教程的后面添加它，但它已经做了很多&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;app = Flask(__name__, instance_relative_config=True)&lt;/code&gt;创建Flask实例&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;__name__&lt;/code&gt;是当前Python模块的的名称。应用程序需要知道它所在的位置以设置一些路径，并&lt;code&gt;__name__&lt;/code&gt;是告诉它的一种方便的方法&lt;/li&gt;
&lt;li&gt;&lt;code&gt;instance_relative_config=True&lt;/code&gt;告诉应用程序配置文件是相对于&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/config/#instance-folders" target="_blank" rel="noopener"
 &gt;instance文件夹&lt;/a&gt;的。这个instance文件夹位于baby包外面并且可以保存本地数据，且数据不能被提交到版本库，例如配置密码和数据库文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Config.from_mapping" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.config.from_mapping()&lt;/strong&gt;&lt;/a&gt;设置一些应用程序将会使用的默认配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/config/#SECRET_KEY" target="_blank" rel="noopener"
 &gt;SECRET_KEY&lt;/a&gt;&lt;/strong&gt; 被Flask和扩展使用用来保证数据的安全。它被设置为&amp;rsquo;dev&amp;rsquo;以在开发期间提供方便的值，但在部署时应该用随机值覆盖它。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DATABASE&lt;/code&gt; 是保存SQLite数据库文件的路径。它位于app.instance_path下，这是Flask为实例文件夹选择的路径。您将在下一篇教程中了解有关数据库的更多信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Config.from_pyfile" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.config.from_pyfile()&lt;/strong&gt;&lt;/a&gt;使用从instance文件夹中的&lt;code&gt;config.py&lt;/code&gt;文件获取的值覆盖默认配置（如果存在）。例如当部署的时候，将被用于设置一个真实的&lt;code&gt;SECRET_KEY&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;test_config&lt;/code&gt;也可以传递给工厂，而不是实例配置。这样您将在本教程后面编写的测试可以独立于您配置的任何开发值进行配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[&lt;strong&gt;os.makedirs()&lt;/strong&gt;]确保&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.instance_path" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;app.instance_path&lt;/strong&gt;&lt;/a&gt;存在。Flask不会自动创建一个instance文件夹，但是它需要被创建，因为你的项目将在这创建SQLite数据库文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.route" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;@app.route()&lt;/strong&gt;&lt;/a&gt;创建了一个简单的路由，以便在进入本教程的其余部分之前可以看到应用程序正常工作。它在URL &lt;code&gt;/hello&lt;/code&gt;和返回响应的函数之间创建了一个连接，在这种情况下响应函数会返回一个字符串&amp;rsquo;Hello，World！'&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="运行应用程序"&gt;运行应用程序
&lt;/h2&gt;&lt;p&gt;现在可以通过Flask命令运行应用程序。通过终端，告诉Flask在哪里找到你的应用程序，然后通过开发者模式运行&lt;/p&gt;
&lt;p&gt;开发者模式，只要页面引发异常，就显示一个交互式调试器，在改变代码的时候重启服务器。您可以让它保持运行，只需按照教程重新加载浏览器页面即可。&lt;/p&gt;
&lt;p&gt;对于Linux和Mac：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export FLASK_ENV&lt;span style="color:#f92672"&gt;=&lt;/span&gt;development
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于Windows cmd，使用&lt;code&gt;set&lt;/code&gt;而不是&lt;code&gt;export&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;set FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;set FLASK_ENV&lt;span style="color:#f92672"&gt;=&lt;/span&gt;development
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于Windows PowerShell，请使用&lt;code&gt;$env&lt;/code&gt;：而不是&lt;code&gt;export&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$env:FLASK_APP &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;baby&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$env:FLASK_ENV &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;development&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你会看到与此类似的输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flask run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Serving Flask app &lt;span style="color:#e6db74"&gt;&amp;#34;baby&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;lazy loading&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Environment: development
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debug mode: on
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Running on http://127.0.0.1:5000/ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Restarting with stat
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debugger is active!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debugger PIN: 390-520-193
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在浏览器中访问&lt;a class="link" href="http://127.0.0.1:5000/hello" target="_blank" rel="noopener"
 &gt;http://127.0.0.1:5000/hello&lt;/a&gt;，您应该看到“Hello，World！”消息。恭喜，您现在正在运行Flask Web应用程序！&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/154" target="_blank" rel="noopener"
 &gt;定义和访问数据库&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 项目布局</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-xiang-mu-bu-ju/</link><pubDate>Tue, 29 Jul 2025 10:09:32 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-xiang-mu-bu-ju/</guid><description>&lt;p&gt;首先创建一个目录，并进入到目录里面&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后参考&lt;a class="link" href="https://www.walkerfree.com/article/146/" target="_blank" rel="noopener"
 &gt;安装说明&lt;/a&gt;来设置Python虚拟环境并且为项目安装Flask&lt;/p&gt;
&lt;p&gt;本教程假设您从现在开始使用baby目录，每个代码块顶部的文件名都与该目录相关。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Flask应用程序可以像单个文件一样简单。&lt;/p&gt;
&lt;p&gt;hello.py&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然而随着项目越来复杂，所有代码放在一个文件将会变得越来越臃肿。Python项目使用包将代码组织成多个模块，可以在需要的地方导入，本教程也会这样做。&lt;/p&gt;
&lt;p&gt;本教程项目目录将会包括如下内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;baby/&lt;/code&gt;，一个包含应用程序代码和文件的Python包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tests/&lt;/code&gt;，一个包含测试模块的目录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env3&lt;/code&gt;，一个Flask和其他依赖库被安装的Python虚拟环境&lt;/li&gt;
&lt;li&gt;安装文件告诉Python如何安装项目。&lt;/li&gt;
&lt;li&gt;版本控制配置，例如git。您应养成给所有项目使用某种类型的版本控制的习惯，无论大小如何。&lt;/li&gt;
&lt;li&gt;您将来可能添加的任何其他项目文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后，您的项目布局将如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/durban/python/baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── tests
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── test_factory.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── test_db.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── test_blog.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── test_auth.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── data.sql
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── conftest.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── setup.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── setup.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── gunicorn.example.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── gunicorn.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── templates
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ ├── blog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ ├── update.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ ├── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ └── create.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ ├── base.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ └── auth
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ ├── register.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ └── login.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── static
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ └── app.css
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── schema.sql
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── db.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── blog.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── auth.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── MANIFEST.in
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你使用了版本控制，应忽略在运行项目时生成的以下文件。根据您使用的编辑器，可能还有其他文件。通常，忽略您未编写的文件。例如，使用git：&lt;/p&gt;
&lt;p&gt;.gitignore&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.env3/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*.pyc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;__pycache__/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;instance/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.pytest_cache/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.coverage
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmlcov/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dist/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;build/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*.egg-info/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/153" target="_blank" rel="noopener"
 &gt;应用程序设置&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 新手教程 - 概览</title><link>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-gai-lan/</link><pubDate>Tue, 29 Jul 2025 10:09:28 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-xin-shou-jiao-cheng-gai-lan/</guid><description>&lt;p&gt;教程内容有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/152" target="_blank" rel="noopener"
 &gt;项目布局&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/153" target="_blank" rel="noopener"
 &gt;应用程序设置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/154" target="_blank" rel="noopener"
 &gt;定义和访问数据库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/155" target="_blank" rel="noopener"
 &gt;Blueprint和视图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/156" target="_blank" rel="noopener"
 &gt;模板&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/157" target="_blank" rel="noopener"
 &gt;静态文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/158" target="_blank" rel="noopener"
 &gt;博客Blueprint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/159" target="_blank" rel="noopener"
 &gt;使项目可安装&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/160" target="_blank" rel="noopener"
 &gt;测试覆盖率&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/161" target="_blank" rel="noopener"
 &gt;部署到生产&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.walkerfree.com/article/162" target="_blank" rel="noopener"
 &gt;继续开发！&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本教程将指导您创建一个名为Baby的基本博客应用程序。用户可以注册，登录，创建帖子，编辑或删除自己的帖子。您将能够在其他计算机上打包并安装该应用程序。 &lt;a class="link" href="https://camo.githubusercontent.com/c178bd4b7a95fd0e85a24a4df1d69d732f48d76b/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433313630392f77616c6b6572667265652f77785f3135315f312e706e67" target="_blank" rel="noopener"
 &gt;&lt;img alt="新手教程" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://camo.githubusercontent.com/c178bd4b7a95fd0e85a24a4df1d69d732f48d76b/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433313630392f77616c6b6572667265652f77785f3135315f312e706e67"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;假设您已熟悉Python。Python文档中的官方教程是首先学习或复习的好方法。&lt;/p&gt;
&lt;p&gt;虽然这个新手教程旨在提供一个良好的起点，但本教程并未涵盖Flask的所有功能。查看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/quickstart/#quickstart" target="_blank" rel="noopener"
 &gt;快速入门&lt;/a&gt;，了解Flask可以做的事情，然后深入了解文档以了解更多信息。本教程仅使用Flask和Python提供的内容。在其他项目中，您可能决定使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/extensions/#extensions" target="_blank" rel="noopener"
 &gt;Extensions&lt;/a&gt;或其他库来简化某些任务。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://camo.githubusercontent.com/e31114c998d002420ce5ee63b5640d1b1ee96d87/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433333039302f77616c6b6572667265652f77785f3135315f322e706e67" target="_blank" rel="noopener"
 &gt;&lt;img alt="新手教程" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://camo.githubusercontent.com/e31114c998d002420ce5ee63b5640d1b1ee96d87/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433333039302f77616c6b6572667265652f77785f3135315f322e706e67"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Flask是很灵活的，它不需要您使用任何特殊的项目或者代码布局。然后，首次使用时，使用更结构化的方法会很有帮助。这意味着该新手教程需要预先考虑一些样板，但这样做是为了避免新开发人员遇到的许多常见陷阱，并创建一个易于扩展的项目。一旦你对Flask更加舒适，你可以走出这个结构并充分利用Flask的灵活性。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://camo.githubusercontent.com/4d9d28efbf22a2e432906986a32c90d2edd8bf68/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433343034312f77616c6b6572667265652f77785f3135315f332e706e67" target="_blank" rel="noopener"
 &gt;&lt;img alt="新手教程" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://camo.githubusercontent.com/4d9d28efbf22a2e432906986a32c90d2edd8bf68/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6479356476637563312f696d6167652f75706c6f61642f76313535383433343034312f77616c6b6572667265652f77785f3135315f332e706e67"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果您希望在学习本教程时将项目与最终产品进行比较，那么&lt;a class="link" href="https://github.com/pallets/flask/tree/1.0.2/examples/tutorial" target="_blank" rel="noopener"
 &gt;本教程项目可作为Flask存储库中的示例提供&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下一期继续 - &lt;a class="link" href="https://www.walkerfree.com/article/152" target="_blank" rel="noopener"
 &gt;项目布局&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Flask 1.0 快速入手 【四】</title><link>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-si/</link><pubDate>Tue, 29 Jul 2025 10:09:23 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-si/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1558062562/walkerfree/wf_150.jpg" alt="Featured image of post Flask 1.0 快速入手 【四】" /&gt;&lt;h2 id="sessions"&gt;Sessions
&lt;/h2&gt;&lt;p&gt;除了请求对象之外，还有一个叫做&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;session&lt;/a&gt;的第二个对象，它允许您将特定于某个用户的信息从一个请求存储到下一个请求。这是在cookie的基础上实现的，并以加密方式对cookie进行签名。这意味着用户可以查看cookie的内容但不能修改它，除非他们知道用于签名的密钥。&lt;/p&gt;
&lt;p&gt;要使用session的话，您必须设置一个密钥。以下是session的工作方式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask, session, redirect, url_for, escape, request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;secret_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\xbc\xf6\x8a&lt;/span&gt;&lt;span style="color:#e6db74"&gt;TP&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\xba\xe7\xf8&lt;/span&gt;&lt;span style="color:#e6db74"&gt;*&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\xcc\xa7\xbe\xb2&lt;/span&gt;&lt;span style="color:#e6db74"&gt;S&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\xd5\x83&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; session:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Logged in as &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; escape(session[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;You are not logged in&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;		&amp;lt;form method=&amp;#34;post&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;			&amp;lt;p&amp;gt;&amp;lt;input type=&amp;#39;text&amp;#39; name=&amp;#39;username&amp;#39; /&amp;gt;&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;			&amp;lt;p&amp;gt;&amp;lt;input type=&amp;#39;submit&amp;#39; value=&amp;#39;Login&amp;#39; /&amp;gt;&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;		&amp;lt;/form&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;	&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/logout&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;logout&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pop(&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你没有使用模板引擎（如本例所示），这里提到的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.escape" target="_blank" rel="noopener"
 &gt;escape()&lt;/a&gt;会为你做转义处理。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="如何生成一个好一点的密钥"&gt;如何生成一个好一点的密钥
&lt;/h3&gt;&lt;p&gt;密钥应尽可能随机。您的操作系统可以基于加密随机生成器生成相当随机的数据。使用以下命令快速生成一个值给Flask.secret_key (或者 &lt;a class="link" href="http://flask.pocoo.org/docs/1.0/config/#SECRET_KEY" target="_blank" rel="noopener"
 &gt;SECRET_KEY&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python -c &lt;span style="color:#e6db74"&gt;&amp;#39;import os; print(os.urandom(16))&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b&lt;span style="color:#e6db74"&gt;&amp;#39;\xbc\xf6\x8aTP\xba\xe7\xf8*\xcc\xa7\xbe\xb2S\xd5\x83&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;关于基于cookie的会话的说明：Flask将获取您放入session对象的值，并将它们序列化为cookie。如果您发现某些值不会在请求中保留，则确实启用了Cookie，并且您没有收到明确的错误消息，请检查页面响应中的Cookie大小与Web浏览器支持的大小相比较。&lt;/p&gt;
&lt;p&gt;除了默认的基于客户端的session，如果你想在服务器端处理session，有几个Flask扩展支持这个。&lt;/p&gt;
&lt;h2 id="即时消息"&gt;即时消息
&lt;/h2&gt;&lt;p&gt;好的应用和用户体验是关于反馈的，如果用户没有得到足够的反馈，他们可能最终会厌恶应用程序，Flask提供了一种使用即时系统向用户提供反馈的简单方法。即时系统基本上可以在请求结束时记录消息，并在下一个（也是下一个）请求中访问它。这通常与布局模板结合使用以显示消息。&lt;/p&gt;
&lt;p&gt;要使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.flash" target="_blank" rel="noopener"
 &gt;flash()&lt;/a&gt;方法添加一个即时消息，要获取即时消息，可以使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.get_flashed_messages" target="_blank" rel="noopener"
 &gt;get_flashed_messages()&lt;/a&gt;的方法，这些消息也可以在模板中使用。查看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/patterns/flashing/#message-flashing-pattern" target="_blank" rel="noopener"
 &gt;即时消息&lt;/a&gt;以获取完整示例。&lt;/p&gt;
&lt;h2 id="日志"&gt;日志
&lt;/h2&gt;&lt;p&gt;有时您可能处理的数据应该是正确的，但事实并非如此。例如，您可能有一些客户端代码向服务器发送HTTP请求，但显然格式不正确。这可能是由用户篡改数据或客户端代码失败引起的。在大多数情况下，在这种情况下回复&lt;code&gt;400 Bad Request&lt;/code&gt;是可以的，但有时不会这样做，而且代码必须继续工作。&lt;/p&gt;
&lt;p&gt;您可能仍想记录发生了可疑的事情。这是loggers派上用场的地方。从Flask 0.3开始，预先配置了一个logger供您使用。&lt;/p&gt;
&lt;p&gt;以下是一些日志调用示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;&amp;#39;A value for debuging&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;warning(&lt;span style="color:#e6db74"&gt;&amp;#39;A warning occurred (&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; apples)&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;error(&lt;span style="color:#e6db74"&gt;&amp;#39;A error occurred&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;附加自带的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.logger" target="_blank" rel="noopener"
 &gt;logger&lt;/a&gt;是标准的&lt;a class="link" href="https://docs.python.org/3/library/logging.html#logging.Logger" target="_blank" rel="noopener"
 &gt;Logger&lt;/a&gt;，因此请访问官方&lt;a class="link" href="https://docs.python.org/library/logging.html" target="_blank" rel="noopener"
 &gt;logging文档&lt;/a&gt;以获取更多信息。&lt;/p&gt;
&lt;p&gt;详细了解&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/errorhandling/#application-errors" target="_blank" rel="noopener"
 &gt;应用程序错误&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="挂钩wsgi中间件"&gt;挂钩WSGI中间件
&lt;/h2&gt;&lt;p&gt;如果要将WSGI中间件添加到应用程序，可以包装内部WSGI应用程序。例如，如果你想使用Werkzeug包中的一个中间件来处理lighttpd中的bug，你可以这样做&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.contrib.fixers &lt;span style="color:#f92672"&gt;import&lt;/span&gt; LighttpdCGIRootFix
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wsgi_app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; LighttpdCGIRootFix(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wsgi_app)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="使用flask-extensions"&gt;使用Flask Extensions
&lt;/h2&gt;&lt;p&gt;扩展程序是帮助您完成常见任务的程序包。例如，Flask-SQLAlchemy提供SQLAlchemy支持，使其易于与Flask一起使用。&lt;/p&gt;
&lt;p&gt;有关Flask扩展的更多信息，请查看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/extensions/#extensions" target="_blank" rel="noopener"
 &gt;扩展&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="部署到web服务器"&gt;部署到Web服务器
&lt;/h2&gt;&lt;p&gt;准备部署新的Flask应用了吗？转到“&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/deploying/#deployment" target="_blank" rel="noopener"
 &gt;部署选项&lt;/a&gt;”。&lt;/p&gt;</description></item><item><title>Flask 1.0 快速入手 【三】</title><link>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-san/</link><pubDate>Tue, 29 Jul 2025 10:09:19 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-san/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1557969152/walkerfree/wf_149.jpg" alt="Featured image of post Flask 1.0 快速入手 【三】" /&gt;&lt;h2 id="cookies"&gt;Cookies
&lt;/h2&gt;&lt;p&gt;你可以通过&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Request.cookies" target="_blank" rel="noopener"
 &gt;cookies&lt;/a&gt;这个属性来访问到cookies，可以使用响应对象的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Response.set_cookie" target="_blank" rel="noopener"
 &gt;set_cookie&lt;/a&gt;方法来设置cookies。请求对象的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Request.cookies" target="_blank" rel="noopener"
 &gt;cookies&lt;/a&gt;属性是一个字典类型，它包含了所有从客户端传递过来的cookies数据。如果你想使用cookies，不要直接使用cookies，而是使用Flask中的&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/quickstart/#sessions" target="_blank" rel="noopener"
 &gt;Sessions&lt;/a&gt;，它为您在cookies的上一层添加了一些安全的处理。&lt;/p&gt;
&lt;p&gt;读取cookies&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;()&lt;span style="color:#960050;background-color:#1e0010"&gt;：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;cookies&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 为了避免键值不存在的情况，使用cookies.get(key)替换cookies[key]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;存储cookies&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; make_response
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; make_response(render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;....&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp&lt;span style="color:#f92672"&gt;.&lt;/span&gt;set_cookie(&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;the other username&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; resp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，cookies是在响应对象中设置的。由于我们通常只是从视图函数返回字符串，Flask会为你将它们转换为响应对象。如果您明确要这样做，可以使用make_response()函数然后修改它。&lt;/p&gt;
&lt;p&gt;有时您可能希望在响应对象尚不存在的位置设置cookie，这可以通过使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/patterns/deferredcallbacks/#deferred-callbacks" target="_blank" rel="noopener"
 &gt;延迟请求回调&lt;/a&gt;模式来实现。&lt;/p&gt;
&lt;p&gt;为此，请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/quickstart/#about-responses" target="_blank" rel="noopener"
 &gt;关于响应&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="重定向和错误"&gt;重定向和错误
&lt;/h2&gt;&lt;p&gt;将用户重定向到另一个端点，使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.redirect" target="_blank" rel="noopener"
 &gt;redirect()&lt;/a&gt;函数。用一个错误代码提前中止请求，使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.abort" target="_blank" rel="noopener"
 &gt;abort()&lt;/a&gt;函数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; abort, redirect, url_for
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; redirect(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;login&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; abort(&lt;span style="color:#ae81ff"&gt;401&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; this_is_never_executed()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是一个毫无意义的例子，因为它将一个用户从index页面重定向到了一个不能访问的401页面，但是这个例子演示了如何使用的。&lt;/p&gt;
&lt;p&gt;默认情况下，每一个错误码都会显示一个黑白的错误页面，如果你想自定义这个错误页面，可以使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.errorhandler" target="_blank" rel="noopener"
 &gt;errorhandler()&lt;/a&gt;装饰器&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; render_template
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.errorhandler&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;page_not_found&lt;/span&gt;(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;page_not_found.html&amp;#39;&lt;/span&gt;, error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;error), &lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.render_template" target="_blank" rel="noopener"
 &gt;render_template()&lt;/a&gt;调用后的&lt;code&gt;404&lt;/code&gt;。这将告诉Flask页面的状态码是404，404意味着页面不存在。默认情况下，页面状态码为200，意思是：一切正常。&lt;/p&gt;
&lt;p&gt;有关详细信息，请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/errorhandling/#error-handlers" target="_blank" rel="noopener"
 &gt;错误处理程序&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="关于响应"&gt;关于响应
&lt;/h2&gt;&lt;p&gt;视图函数返回的值被自动转换为一个响应对象。如果返回值是一个字符串，它将被转换为一个带有该字符串的响应对象作为响应体，状态码200和一个text/htmlMIME类型。Flask转换返回值为一个响应对象的逻辑如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果返回一个正确类型的响应对象，这个响应对象将会从视图函数直接返回。&lt;/li&gt;
&lt;li&gt;如果返回一个字符串，一个影响对象将携带改字符串和默认的参数被创建。&lt;/li&gt;
&lt;li&gt;如果返回元组，则元组中的项可以提供额外信息。这样的元组必须采用&lt;code&gt;(response, status, headers)&lt;/code&gt;或&lt;code&gt;(response, headers)&lt;/code&gt;的形式，其中至少有一个项目必须在元组中。&lt;code&gt;status&lt;/code&gt;值将覆盖状态代码，&lt;code&gt;headers&lt;/code&gt;可以是其他标头值的列表或字典。&lt;/li&gt;
&lt;li&gt;如果这些都不起作用，Flask将假定返回值是有效的WSGI应用程序并将其转换为响应对象。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果要在视图中获取生成的响应对象，可以使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.make_response" target="_blank" rel="noopener"
 &gt;make_response()&lt;/a&gt;函数。&lt;/p&gt;
&lt;p&gt;假如你有一个像下面这样的视图&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.errorhandler&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;not_found&lt;/span&gt;(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;error.html&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;您只需要使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.make_response" target="_blank" rel="noopener"
 &gt;make_response()&lt;/a&gt;包装返回表达式并获取响应对象以对其进行修改，然后将其返回：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.errorhandler&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;not_found&lt;/span&gt;(error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; make_response(render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;error.html&amp;#39;&lt;/span&gt;), &lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resp&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#39;X-Something&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;A value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; resp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 1.0 快速入手 【二】</title><link>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-er/</link><pubDate>Tue, 29 Jul 2025 10:09:16 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-er/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1556417065/walkerfree/wf_148.jpg" alt="Featured image of post Flask 1.0 快速入手 【二】" /&gt;&lt;h2 id="静态文件"&gt;静态文件
&lt;/h2&gt;&lt;p&gt;动态web应用也需要静态文件。通常包括CSS和Javascript文件。理想情况下，你的服务器配置为为你自己服务的，但是在开发环境Flask也能做到这些，只要创建一个static文件夹在包里面或者在你模块的旁边创建一个static文件夹，在应用中通过&lt;code&gt;/static&lt;/code&gt;调用的方式就会起作用&lt;/p&gt;
&lt;p&gt;为了给static文件生成一个URLs，可以使用一个特殊的&lt;code&gt;static&lt;/code&gt;节点，如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;static&amp;#39;&lt;/span&gt;, filename&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;style.css&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="模板渲染"&gt;模板渲染
&lt;/h2&gt;&lt;p&gt;从Python中生成HTML并不好玩，而且实际上非常麻烦，你必须自己进行HTML转义以保证应用程序的安全。因此Flask自动为你配置了&lt;a class="link" href="http://jinja.pocoo.org/" target="_blank" rel="noopener"
 &gt;Jinja2&lt;/a&gt;模板引擎。&lt;/p&gt;
&lt;p&gt;为了渲染一个模板你可以使用 &lt;strong&gt;render_template()&lt;/strong&gt; 方法。你必须传递一个模板的名字和你想要传递给模板的变量到模板引擎作为关键字参数。下面这个是一个简单例子，演示了如何渲染一个模板&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; render_template
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/hello&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/hello/&amp;lt;name&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;hello.html&amp;#39;&lt;/span&gt;, name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;name)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Flask将在&lt;code&gt;templates&lt;/code&gt;文件夹中寻找对应的模板，如果你的应用是一个模块，这个文件夹将在模块的同一级，如果你的应用是一个包的话，则这个文件夹应该在包的里面，如下例子 例子1： 一个模块&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/application.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/templates
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /hello.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例子2：一个包&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/application
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /__init__py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /templates
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /hello.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于模板，您可以使用Jinja2模板的全部功能。有关更多信息，请访问官方&lt;a class="link" href="http://jinja.pocoo.org/docs/templates" target="_blank" rel="noopener"
 &gt;Jinja2模板文档&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下面是一个模板的例子&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	Hello from Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% if name %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;Hello {{ name }}!&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% else %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&amp;lt;&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;Hello, World!&amp;lt;/&lt;span style="color:#f92672"&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endif %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在模板内部你可以访问&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.request" target="_blank" rel="noopener"
 &gt;request&lt;/a&gt;,&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.session" target="_blank" rel="noopener"
 &gt;session&lt;/a&gt;和&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.g" target="_blank" rel="noopener"
 &gt;g&lt;/a&gt;[1]对象，并且还可以使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.get_flashed_messages" target="_blank" rel="noopener"
 &gt;get_flasjed_message()&lt;/a&gt;函数&lt;/p&gt;
&lt;p&gt;模板的继承是非常有用的。如果你想要知道如何工作的，可以访问&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/patterns/templateinheritance/#template-inheritance" target="_blank" rel="noopener"
 &gt;模板继承&lt;/a&gt;模式文档。基本上，模板继承可以在每个页面上保留某些元素（例如头部，导航和底部）&lt;/p&gt;
&lt;p&gt;模板启动了自动转移功能，因为如果name中含有HTML，则会自动转义。如果你信任一个变量并且你知道它是一个安全的HTML（例如，它来自将wiki标记转换为HTML的模块）你可以使用Markup类标记他是安全的，或者通过使用&lt;code&gt;|safe&lt;/code&gt;过滤下在模板中，有关更多示例，请访问&lt;a class="link" href="http://jinja.pocoo.org/" target="_blank" rel="noopener"
 &gt;Jinja 2文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下面是一个简单的介绍，关于如何使用Markup类&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; from flask import Markup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; Markup&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;string&amp;gt;Hello %s!&amp;lt;/strong&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; % &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;blink&amp;gt;hacker&amp;lt;/blink&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Markup&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;string&amp;gt;Hello &amp;amp;lt;blink&amp;amp;gt;hacker&amp;amp;lt;/blink&amp;amp;gt;!&amp;lt;/strong&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; Markup.escape&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;blink&amp;gt;hacker&amp;lt;/blink&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Markup&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;amp;lt;blink&amp;amp;gt;hacker&amp;amp;lt;/blink&amp;amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; Markup&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;em&amp;gt;Marked up&amp;lt;/em&amp;gt; &amp;amp;raquo; Html&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;.striptags&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Marked up » Html&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;更改日志 在0.5版本修改：不再为所有的模板启动自动转义。以&lt;code&gt;.html&lt;/code&gt;,&lt;code&gt;.htm&lt;/code&gt;,&lt;code&gt;.xml&lt;/code&gt;,&lt;code&gt;.xhtml&lt;/code&gt;扩展的模板将自动触发转义过滤。从字符串加载的模板将禁用自动转义。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;[1]&lt;/strong&gt; 不知道g对象是什么？它是一个你能存储你需要的信息的东西,有关详细信息，请查看该对象的文档（&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.g" target="_blank" rel="noopener"
 &gt;g&lt;/a&gt;）和&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/patterns/sqlite3/#sqlite3" target="_blank" rel="noopener"
 &gt;使用带有Flask的SQLite 3&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="访问请求数据"&gt;访问请求数据
&lt;/h2&gt;&lt;p&gt;对于web应用程序，对客户端发送到服务器的数据做出相应是非常重要的，在Flask中这些信息通过全局&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.request" target="_blank" rel="noopener"
 &gt;request&lt;/a&gt;对象提供。如果你有一些Python开发经验，你也许想要知道改对象如何是全局的以及如何Flask如何设置他仍然是线程安全的。答案是上下文本地化&lt;/p&gt;
&lt;h3 id="上下文本地化"&gt;上下文本地化
&lt;/h3&gt;&lt;hr&gt;
&lt;p&gt;如果你想知道它是如何工作的，并且想知道它是如何实现的，请阅读这部分否则跳过继续&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Flask中的某些对象是全局对象，但是不是通常的对象。这些对象实际上是一个协议，而且能够本地化到一个特定的上下文。非常绕，但是实际上很容易理解。&lt;/p&gt;
&lt;p&gt;想象下，这个上下文是一个处理中的线程。一个请求过来后服务器端决定产生一个新的线程（或其他东西，底层对象能够处理除线程之外的并发系统）。当Flask启动它的内部请求去处理它时，他会判断出当前线程是活动上下文并将当前应用程序和WSGI环境绑定到改上下文（线程）。它以智能的方式实现了这个逻辑，以便一个应用程序可以在不中断情况下调用另一个应用程序。&lt;/p&gt;
&lt;p&gt;这对于你意味着什么呢？基本上你能完全忽略这个情况，除非你做了一些事情比如单元测试，你将注意到因为没有请求对象导致依赖于对象请求的代码将突然中断。这个解决方案就是你自己创建了一个请求对象然后绑定它到上下文，最简单的单元测试解决方案是使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.test_request_context" target="_blank" rel="noopener"
 &gt;test_request_context()&lt;/a&gt;上下文管理器。结合&lt;code&gt;with&lt;/code&gt;语句时，它将会绑定一个测试请求使你能够跟它进行交互。下面是一个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context(&lt;span style="color:#e6db74"&gt;&amp;#39;/hello&amp;#39;&lt;/span&gt;, method&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/hello&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其他可能的情况是传入整个WSGI环境的到request_context()方法&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;request_context(env):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="请求对象"&gt;请求对象
&lt;/h2&gt;&lt;p&gt;请求对象被记录在API部分，这里暂时不做全覆盖的介绍，具体详情可以看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Request" target="_blank" rel="noopener"
 &gt;Request&lt;/a&gt;或者看我后面的介绍，这里暂时只介绍下普通情况下用的比较频繁的操作，首先我们必须从flask模块导入它&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过使用method属性可以获取当前请求的方式。为了访问到表单数据（数据通过&lt;code&gt;POST&lt;/code&gt;或者&lt;code&gt;PUT&lt;/code&gt;方式请求）你可以使用form属性。这里是一个完整的例子，关于这两个属性的&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; valid_login(request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;], request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;]):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; log_the_user_in(request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;form[&lt;span style="color:#e6db74"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Invalid username/password&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(&lt;span style="color:#e6db74"&gt;&amp;#39;login.html&amp;#39;&lt;/span&gt;, error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;error)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果form的属性中没有对应的key会发生什么呢？这种情况下将会抛出一个KeyError异常。你可以像标准KeyError一样捕获它，但如果不这样做，则会显示HTTP 400错误请求错误页面。所以很多情况下你没必要处理这个问题。&lt;/p&gt;
&lt;p&gt;要访问URL(?key=value)中提交的参数，可以使用args属性&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;建议使用get或捕获KeyError来访问URL参数，因为用户可能会更改URL并向他们显示400个错误请求页面，在这种情况下体验并不友好。&lt;/p&gt;
&lt;p&gt;有关请求对象的方法和属性的完整列表，请转到&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Request" target="_blank" rel="noopener"
 &gt;Request&lt;/a&gt;文档。&lt;/p&gt;
&lt;h2 id="文件上传"&gt;文件上传
&lt;/h2&gt;&lt;p&gt;你能很容易的使用Flask处理上传的文件。只要确保在提交的表单中不要忘记设置&lt;code&gt;enctype=&amp;quot;multipart/form-data&amp;quot;&lt;/code&gt;属性，否则浏览器将不会传递你的提交的文件&lt;/p&gt;
&lt;p&gt;上传到文件存储在内存或者文件系统的一个临时的位置。你能通过request对象的&lt;strong&gt;files&lt;/strong&gt;这个属性访问这些文件。每个上传的文件存储在一个字典中。它的行为就想标准的Python文件对象一样，但是也有一个**save()**方法，这个防范允许你将文件存储到服务器的文件系统中。下面是一个简单的演示例子&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/upload&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;upload_file&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;files[&lt;span style="color:#e6db74"&gt;&amp;#39;file&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;save(&lt;span style="color:#e6db74"&gt;&amp;#39;/var/www/uploads/upload.txt&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您想知道文件在上传到您的应用程序之前在客户端上的命名方式，你可以通过访问filename属性。但请记住，这个价值可以伪造，所以永远不要相信这个价值。如果你想要使用客户端的文件名去存储到服务器上，通过Werkzeug为您提供的**secure_filename()**函数传递它。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; werkzeug.utils &lt;span style="color:#f92672"&gt;import&lt;/span&gt; secure_filename
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/upload&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;upload_file&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;files[&lt;span style="color:#e6db74"&gt;&amp;#39;file&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;save(&lt;span style="color:#e6db74"&gt;&amp;#39;/var/www/upload/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; secure_filename(f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filename))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有关更好的示例，请查看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/patterns/fileuploads/#uploading-files" target="_blank" rel="noopener"
 &gt;上传文件&lt;/a&gt;章节。&lt;/p&gt;</description></item><item><title>Flask 1.0 快速入手 【一】</title><link>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-yi/</link><pubDate>Tue, 29 Jul 2025 10:09:13 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-kuai-su-ru-shou-yi/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1556014814/walkerfree/wf_147.jpg" alt="Featured image of post Flask 1.0 快速入手 【一】" /&gt;&lt;p&gt;是不是已经很想快点尝试下Flask了，本次分享将尽量全面的介绍Flask，继续下面的内容之前请确保您已经安装了Flask，如果你还没有安装的话，请回到前面的部分“Flask 1.0安装”再了解下如何安装。&lt;/p&gt;
&lt;h2 id="一个小的应用"&gt;一个小的应用
&lt;/h2&gt;&lt;p&gt;一个小应用的代码看起来像是下面这个样子&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello_world&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面解释下这段代码做了什么&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先我们导入了&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;Flask&lt;/a&gt;类，这个类的实例将是我们WSGI应用的实例&lt;/li&gt;
&lt;li&gt;下面创建这个类的实例。第一个参数是应用的模块或者包的名称。如果你使用一个单独的模块（比如这里的例子），你应该使用&lt;code&gt;__name__&lt;/code&gt;，因为这个取决于应用是否作为一个应用程序启动还是作为一个模块导入，它的名称将是不同的（&lt;code&gt;__main__&lt;/code&gt;与实际导入的名称相对应）。这个是必须要传入的参数，会让Flask知道去哪里查找模板、静态文件等等，有关于更多的信息可以查看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask" target="_blank" rel="noopener"
 &gt;Flask&lt;/a&gt;的官方文档&lt;/li&gt;
&lt;li&gt;我们使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.Flask.route" target="_blank" rel="noopener"
 &gt;route()&lt;/a&gt;装饰器去告诉Flask什么样的URL路由将会触发我们的函数&lt;/li&gt;
&lt;li&gt;hello_world这个函数的名称也是有用的，后面在生成URLs的时候会有特殊的功能，而且函数的返回值将展示在用户的浏览器上。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;只需要保存上面的代码到文件hello.py或者是其他的名称带有后缀.py的文件即可，同时确保文件的名称不能叫做flask.py，因为这样会是的自己的文件脚本跟Flask自己冲突。&lt;/p&gt;
&lt;p&gt;为了运行应用程序，你既可以使用flask命令也可以使用python的-m启动Flask.不过在你运行程序前你应该在终端导入一个环境变量FALSK_APP，如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hello.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flask run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Serving Flask app &lt;span style="color:#e6db74"&gt;&amp;#34;hello.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Environment: production
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; WARNING: Do not use the development server in a production environment.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Use a production WSGI server instead.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debug mode: off
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Running on http://127.0.0.1:5000/ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您使用的是Windows，则环境变量语法取决于命令行解释程序。在命令提示符上:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;ath&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;o&lt;span style="color:#ae81ff"&gt;\a&lt;/span&gt;pp&amp;gt;set FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hello.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在PowerShell上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PS C:&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;ath&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;o&lt;span style="color:#ae81ff"&gt;\a&lt;/span&gt;pp&amp;gt; $env:FLASK_APP &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hello.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;另外你也可以使用&lt;code&gt;python -m flask&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export FLASK_APP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hello.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python -m flask run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Serving Flask app &lt;span style="color:#e6db74"&gt;&amp;#34;hello.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Environment: production
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; WARNING: Do not use the development server in a production environment.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Use a production WSGI server instead.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Debug mode: off
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; * Running on http://127.0.0.1:5000/ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="外部可见服务器"&gt;外部可见服务器
&lt;/h3&gt;&lt;p&gt;这将运行一个非常简单的内置服务器，对于开发和测试是足够用了，但是不是你想在生产环境使用的东西。后面会有相关部署环节的介绍。&lt;/p&gt;
&lt;p&gt;如果你启动了上面的程序的话，你将会注意到，运行起来的服务只能你自己的电脑才能访问，网络上其他的电脑是没有办法访问的。在debug模式中这是默认的行为，只允许你自己在你自己的电脑上随意的执行Python代码&lt;/p&gt;
&lt;p&gt;如果你禁止了debug模式或者新人在在同一网络上的用户，你可以公开你的服务，只需要简单的在命令行中加入&lt;code&gt;--host=0.0.0.0&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flask run --host&lt;span style="color:#f92672"&gt;=&lt;/span&gt;0.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个操作将会告诉你的电脑，去监听所有公开的网络ip&lt;/p&gt;
&lt;h2 id="如果服务没有启动该做什么"&gt;如果服务没有启动该做什么
&lt;/h2&gt;&lt;p&gt;一旦&lt;code&gt;python -m flask&lt;/code&gt;启动失败或者flask不存在，这总情况可能有多种原因，首先你需要仔细看一下报错的错误信息&lt;/p&gt;
&lt;h3 id="旧的flask版本"&gt;旧的flask版本
&lt;/h3&gt;&lt;p&gt;Flask版本小于0.11的话会有不同的方式启动应用，简而言之，flask命令不存在，而且也没有&lt;code&gt;python -m flask&lt;/code&gt;。这种情况下有两种选择，一是将Flask升级到新版本，二是看下这个&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/server/#server" target="_blank" rel="noopener"
 &gt;开发服务器&lt;/a&gt;文档遭到启动服务器的替代方法&lt;/p&gt;
&lt;h3 id="导入名称无效"&gt;导入名称无效
&lt;/h3&gt;&lt;p&gt;FLASK_APP环境变量是在&lt;code&gt;flask run&lt;/code&gt;的时候导入的模块的名称，在这种情况下，模块的名称是错误的话你将得到一个启动时导入错误提示（或者，如果导航到应用程序时启用了调试），它将告诉你试图导入的内容以及失败的原因。&lt;/p&gt;
&lt;p&gt;最常见的错误是拼写错误或者是因为你实际上并没有创建一个应用程序对象&lt;/p&gt;
&lt;h2 id="调试模式"&gt;调试模式
&lt;/h2&gt;&lt;p&gt;(想要记录错误和堆栈跟踪？请看&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/errorhandling/#application-errors" target="_blank" rel="noopener"
 &gt;应用程序错误&lt;/a&gt;) flask脚本能够很适合启动一个本地的开发服务器，但是每次更改代码后都需要重新启动服务器。这不是很好，Flask能做的更好，如果启动了调试支持，服务器将在代码改动后自动重新加载，如果出现问题，他还会提供一个有用的调试器。&lt;/p&gt;
&lt;p&gt;为了启用所有开发功能（包括调试模式），你需要在启动服务器前导入FLASK_ENV环境变量并且设置它的值为&lt;code&gt;development&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export FLASK_ENV&lt;span style="color:#f92672"&gt;=&lt;/span&gt;development
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flask run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（在Windows环境中需要用&lt;code&gt;set&lt;/code&gt;代替&lt;code&gt;export&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;这样做有以下几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它激活了调试器&lt;/li&gt;
&lt;li&gt;它激活了自动重新加载器&lt;/li&gt;
&lt;li&gt;它启用了Flask应用程序上的调试模式。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你也能通过导入变量FLASK_DEBUG=1来单独控制调试模式&lt;/p&gt;
&lt;p&gt;在&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/server/#server" target="_blank" rel="noopener"
 &gt;开发服务器&lt;/a&gt;中有更多关于参数的介绍&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="注意"&gt;注意
&lt;/h3&gt;&lt;p&gt;尽管交互调试器不能在工作在分叉环境中（几乎不可能在生产环境中使用），他仍然允许执行任务的代码，这就使得服务器非常的不安全，所以绝对不要在生产服务上使用&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下面是一个调试器的截图&lt;/p&gt;
&lt;p&gt;&lt;img alt="文章 - Walkerfree" class="gallery-image" data-flex-basis="225px" data-flex-grow="94" height="2712" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1556014807/walkerfree/wx_141_1.png" srcset="https://zhangdapeng.dev/wx_141_1_8207877304705074214_hu_ba7ebcfd49cfae59.png 800w, https://zhangdapeng.dev/wx_141_1_8207877304705074214_hu_57d41930e9b28be9.png 1600w, https://zhangdapeng.dev/wx_141_1_8207877304705074214_hu_67c439082593196e.png 2400w, https://res.cloudinary.com/dy5dvcuc1/image/upload/v1556014807/walkerfree/wx_141_1.png 2550w" width="2550"&gt;&lt;/p&gt;
&lt;p&gt;有关使用调试器的更多信息，请参阅&lt;a class="link" href="http://werkzeug.pocoo.org/docs/debug/#using-the-debugger" target="_blank" rel="noopener"
 &gt;Werkzeug文档&lt;/a&gt;。 还有另一个调试器吗？请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/errorhandling/#working-with-debuggers" target="_blank" rel="noopener"
 &gt;使用调试器&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="路由"&gt;路由
&lt;/h2&gt;&lt;p&gt;现代的web应用使用更加有意义的URLs去帮助用户，如果页面使用有意义的并且用户可以记住的URL，用户可能会更喜欢这个页面并返回来直接访问该页面。&lt;/p&gt;
&lt;p&gt;使用route()装饰器来绑定一个函数到一个URL地址上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Index Page&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/hello&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你可以做得更多！可以将URL的一部分动态化并将多个规则附加到函数。&lt;/p&gt;
&lt;h3 id="变量规则"&gt;变量规则
&lt;/h3&gt;&lt;p&gt;使用&lt;code&gt;&amp;lt;variable_name&amp;gt;&lt;/code&gt;标记部分添加变量部分到URL。你的函数将接收到&lt;code&gt;&amp;lt;variable_name&amp;gt;&lt;/code&gt;作为关键字参数。你也可以一个转换器，来定义具体的参数类型像这样&lt;code&gt;&amp;lt;converter:variable_name&amp;gt;&lt;/code&gt;(可选)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/user/&amp;lt;username&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;show_user_profile&lt;/span&gt;(username):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;User &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; username
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/post/&amp;lt;int:post_id&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;show_post&lt;/span&gt;(post_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Post &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; post_id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/path/&amp;lt;path:subpath&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;show_subpath&lt;/span&gt;(subpath):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Showpath &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; subpath
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;转换器类型：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;类型&lt;/th&gt;
					&lt;th&gt;描述&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;string&lt;/td&gt;
					&lt;td&gt;接收任意字符串除了斜杠（默认情况下接收string类型）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;int&lt;/td&gt;
					&lt;td&gt;接收正整数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;float&lt;/td&gt;
					&lt;td&gt;接收正浮点值&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;path&lt;/td&gt;
					&lt;td&gt;类似string但是也接受斜杠&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;uuid&lt;/td&gt;
					&lt;td&gt;接收UUID字符串&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="唯一url地址重定向行为"&gt;唯一URL地址/重定向行为
&lt;/h3&gt;&lt;p&gt;下面的两个规则在使用尾部斜杠的时候有所不同&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/projects/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;projects&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;The project page.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/about&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;about&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;The about page.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;projects&lt;/code&gt;的规范路由最后部分有斜杠，类似于一个文件系统的文件夹。如果你访问这个URL地址不带有最后的斜杠，Flask则重定向到一个尾部带有斜杠的规范路由&lt;/p&gt;
&lt;p&gt;&lt;code&gt;about&lt;/code&gt;的规范路由最后部分没有斜杠，类似于一个文件的文件路径名。如果你访问这个URL地址最后带有斜杠的话，会产生一个404“Not Found”错误。这有助于保持URL对这些资源的唯一性，并且保持搜索引擎避免两次索引同一页面&lt;/p&gt;
&lt;h3 id="url构建"&gt;URL构建
&lt;/h3&gt;&lt;p&gt;为了构建一个URL地址到一个具体的函数，使用&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;url_for()&lt;/a&gt;函数。它接收一个函数的名字作为第一个参数和任意数量的关键字参数，每一个对应到URL规则变量的对应部分。未知的变量部分作为查询参数附加到URL&lt;/p&gt;
&lt;p&gt;为什么要使用URL转换函数&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/api/#flask.url_for" target="_blank" rel="noopener"
 &gt;url_for()&lt;/a&gt;来构建URL，而不是用硬编码的方式写在模板里面&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;转换要比硬编码十分具有描述性&lt;/li&gt;
&lt;li&gt;你可以一次性修改网址而不比记住它手动去修改硬编码的URL网址&lt;/li&gt;
&lt;li&gt;URL构建处理特殊字符和Unicode数据的转义&lt;/li&gt;
&lt;li&gt;产生的网址路径都是绝对的，避免了不必要的相对路径的麻烦&lt;/li&gt;
&lt;li&gt;如果您的应用程序位于URL根目录之外，例如，在&lt;code&gt;/myapplication&lt;/code&gt;而不是&lt;code&gt;/&lt;/code&gt;，&lt;code&gt;url_for()&lt;/code&gt;能够妥善处理这些问题&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如下面我们使用&lt;code&gt;test_request_context()&lt;/code&gt;方法来测试&lt;code&gt;url_for().test_request_context()&lt;/code&gt;告诉Flask。即使我们使用Python shell，它也会像处理请求一样。 请参阅&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/quickstart/#context-locals" target="_blank" rel="noopener"
 &gt;上下文本地&lt;/a&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask, url_for
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Index&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Login page&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/user/&amp;lt;username&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;profile&lt;/span&gt;(username):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;s profile&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(username)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;test_request_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;login&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;login&amp;#39;&lt;/span&gt;, next&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(url_for(&lt;span style="color:#e6db74"&gt;&amp;#39;profile&amp;#39;&lt;/span&gt;, username&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Durban&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/login
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/login?next&lt;span style="color:#f92672"&gt;=&lt;/span&gt;%2F
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/user/Durban
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="http方法"&gt;HTTP方法
&lt;/h3&gt;&lt;p&gt;Web应用程序在访问URL时使用不同的HTTP方法，在使用Flask开发时，你自己应该熟悉HTTP方法，默认情况下，一个路由只回应一个GET请求，你可以使用route()装饰器的第二个参数来处理不同的HTTP方法&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;login&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;do the login&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;show the login form&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果存在GET，Flask会自动添加对HEAD方法的支持，并根据&lt;a class="link" href="https://www.ietf.org/rfc/rfc2068.txt" target="_blank" rel="noopener"
 &gt;HTTP RFC&lt;/a&gt;处理HEAD请求。同样，OPTIONS会自动为您实施&lt;/p&gt;</description></item><item><title>Flask 1.0 - 安装</title><link>https://zhangdapeng.dev/p/flask-1-0-an-zhuang/</link><pubDate>Tue, 29 Jul 2025 10:09:10 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-an-zhuang/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/v1555568104/walkerfree/wf_146.jpg" alt="Featured image of post Flask 1.0 - 安装" /&gt;&lt;h2 id="python版本要求"&gt;Python版本要求
&lt;/h2&gt;&lt;p&gt;Flask从1.0开始支持Python 3，Flask支持Python 3.4和最新版本，以及Python 2.7和PyPy，同时官方建议使用Python 3的最新版本，由此可以看出Flask 从1.0开始拥抱Python 3&lt;/p&gt;
&lt;h2 id="包依赖"&gt;包依赖
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://werkzeug.pocoo.org/" target="_blank" rel="noopener"
 &gt;Werkzeug&lt;/a&gt; 实现WSGI，它是应用程序和服务器之间的标准Python接口&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://jinja.pocoo.org/" target="_blank" rel="noopener"
 &gt;Jinja&lt;/a&gt; 一种模板语言，用于呈现应用程序所服务的页面&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pypi.org/project/MarkupSafe/" target="_blank" rel="noopener"
 &gt;MarkupSafe&lt;/a&gt; 附带Jinja. 它在渲染模板时逃避不受信任的输入以避免注入攻击。&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pythonhosted.org/itsdangerous/" target="_blank" rel="noopener"
 &gt;ItsDangerous&lt;/a&gt; 安全地签署数据以确保其完整性。这用于保护Flask的会话cookie。&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://click.pocoo.org/" target="_blank" rel="noopener"
 &gt;Click&lt;/a&gt; 是一个用于编写命令行应用程序的框架。它提供flask命令并允许添加自定义管理命令。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些包会在安装Flask后自动安装&lt;/p&gt;
&lt;h2 id="其他可选包"&gt;其他可选包
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://pythonhosted.org/blinker/" target="_blank" rel="noopener"
 &gt;Blinker&lt;/a&gt; 为&lt;a class="link" href="http://flask.pocoo.org/docs/1.0/signals/#signals" target="_blank" rel="noopener"
 &gt;Signals&lt;/a&gt;提供支持.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://simplejson.readthedocs.io/" target="_blank" rel="noopener"
 &gt;SimpleJSON&lt;/a&gt; 是一个快速的JSON实现，与Python的json模块兼容。如果安装了JSON操作，则首选它。&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/theskumar/python-dotenv#readme" target="_blank" rel="noopener"
 &gt;python-dotenv&lt;/a&gt; 在运行flask命令时启用对 &lt;a class="link" href="http://flask.pocoo.org/docs/1.0/cli/#dotenv" target="_blank" rel="noopener"
 &gt;dotenv的环境变量&lt;/a&gt; 的支持。&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pythonhosted.org/watchdog/" target="_blank" rel="noopener"
 &gt;Watchdog&lt;/a&gt; 为开发服务器提供更快，更高效的重新加载器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上的可选包不会自动安装，不过，如果你安装了的话，Flask将自动监测并使用他们&lt;/p&gt;
&lt;h2 id="虚拟环境"&gt;虚拟环境
&lt;/h2&gt;&lt;p&gt;建议在开发和生产中使用虚拟环境来管理你项目的依赖库。&lt;/p&gt;
&lt;p&gt;虚拟环境能解决什么问题呢？如果你拥有Python的项目越多，你需要使用不同版本的Python库的可能性就越大，甚至是Python本身。一个较新版本的Python库可能会打破另一个项目的兼容性&lt;/p&gt;
&lt;p&gt;虚拟环境是独立的Python库组合，每个项目一个。一个项目安装的软件包不会影响其他项目或操作系统的软件包。&lt;/p&gt;
&lt;p&gt;Python 3已经绑定了&lt;a class="link" href="https://docs.python.org/3/library/venv.html#module-venv" target="_blank" rel="noopener"
 &gt;venv&lt;/a&gt;模块用来创建虚拟环境。如果你使用的是Python 3，可以继续下面的操作。&lt;/p&gt;
&lt;p&gt;如果你使用的是Python 2，首先先看下“安装virtualenv”&lt;/p&gt;
&lt;h3 id="创建虚拟环境"&gt;创建虚拟环境
&lt;/h3&gt;&lt;p&gt;创建一个项目目录然后添加一个.venv_baby文件夹，当然这个文件夹你也可以根据自己的需要来定义：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd baby
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python3 - venv .venv_baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Windows环境下的操作&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;py -3 -m venv .venv_baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您使用的是旧版本的Python，您需要安装virtualenv，请使用一下命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;virtualenv .venv_baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Windows环境下的操作&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;\P&lt;/span&gt;ython27&lt;span style="color:#ae81ff"&gt;\S&lt;/span&gt;cripts&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;irtualenv.exe .venv_baby
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="激活环境"&gt;激活环境
&lt;/h3&gt;&lt;p&gt;在你的项目中工作前，激活相应的环境&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;. .venv_baby/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Windows环境下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.venv_baby&lt;span style="color:#ae81ff"&gt;\S&lt;/span&gt;cripts&lt;span style="color:#ae81ff"&gt;\a&lt;/span&gt;ctivate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你的shell提示中将显示激活环境的名称&lt;/p&gt;
&lt;h2 id="安装flask"&gt;安装Flask
&lt;/h2&gt;&lt;p&gt;在激活的环境中使用下面的命令安装Flask&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install Flask
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您希望在发布之前使用最新的Flask代码，请安装或更新主分支中的代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install -U https://github.com/pallets/flask/archive/master.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="安装virtualenv"&gt;安装virtualenv
&lt;/h2&gt;&lt;p&gt;如果你使用的是Python 2，venv模块是不可用的，因此需要安装virtualenv 在Linux系统中，virtualenv由包管理器提供&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Debian, Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt-get install python-virtualenv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# CentOS, Fedora&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo yum install python-virtualenv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Arch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo pacman -S python-virtualenv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你的系统是Mac OS或者Windows，先下载&lt;a class="link" href="https://bootstrap.pypa.io/get-pip.py" target="_blank" rel="noopener"
 &gt;get-pip.py&lt;/a&gt;，然后执行下面的代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo python2 Downloads/get-pip.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo python2 -m pip install virtualenv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Windows系统上，已administrator的身份执行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;\P&lt;/span&gt;ython27&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;ython.exe Downloads&lt;span style="color:#ae81ff"&gt;\g&lt;/span&gt;et-pip.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;\P&lt;/span&gt;ython27&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;ython.exe -m pip install virtualenv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在返回到上面的“创建虚拟环境”.&lt;/p&gt;</description></item><item><title>Flask 1.0 生产环境部署记录</title><link>https://zhangdapeng.dev/p/flask-1-0-sheng-chan-huan-jing-bu-shu-ji-lu/</link><pubDate>Tue, 29 Jul 2025 10:09:06 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-1-0-sheng-chan-huan-jing-bu-shu-ji-lu/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/c_scale,w_520/v1554886328/walkerfree/wf_41.jpg" alt="Featured image of post Flask 1.0 生产环境部署记录" /&gt;&lt;p&gt;Flask 1.0 开始支持Python3啦，一直在使用Flask构建自己的博客，今天重温看了下Flask1.0，感觉还是有很多优点的，随后根据官网的教程搞了一个简易的小博客，整体使用下来，还算是比较轻便的。唯独在部署上不是那么的轻松，但是经过查询也算是找到了解决办法，跟之前的Flask版本不同的是，我这次想试下Gunicorn+Nginx+Supervisor。&lt;/p&gt;
&lt;p&gt;安装的部分就不用我多说，我是把Gunicorn安装在了项目目录下，实际上作为一个项目的话Gunicorn是没必要放在项目中的，毕竟一个是服务层一个是逻辑层。&lt;/p&gt;
&lt;p&gt;为了Gunicorn的启动配置项简单明了，最好是建立一个配置文件，这样下次也能清楚里面启动的时候配置了哪些参数，网上找了一个配置，仅供参考&lt;/p&gt;
&lt;p&gt;gunicorn.conf&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 并行工作进程数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;workers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 指定每个工作者的线程数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;threads &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 监听内网端口5000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bind &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1:8000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 设置守护进程,将进程交给supervisor管理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;daemon &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;false&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 设置最大并发量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;worker_connections &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 设置进程文件目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pidfile &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/var/tmp/gunicorn.pid&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 设置访问日志和错误信息日志路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;accesslog &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/var/tmp/gunicorn_access.log&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;errorlog &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/var/tmp/gunicorn_error.log&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 设置日志记录水平&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;loglevel &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;warning&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的配置注意daemon为false&lt;/p&gt;
&lt;p&gt;然后添加supervisor的配置，这里要做的只是简单的添加supervisor配置，至于supervisor的安装还是很简单的，mac下建议使用brew安装&lt;/p&gt;
&lt;p&gt;贴出一份我这边测试时使用的配置&lt;/p&gt;
&lt;p&gt;baby.ini&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;program:baby&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;directory &lt;span style="color:#f92672"&gt;=&lt;/span&gt; /Users/durban/python/baby ; 程序的启动目录
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;command &lt;span style="color:#f92672"&gt;=&lt;/span&gt; gunicorn -c /Users/durban/python/baby/gunicorn.conf baby ; 启动命令，可以看出与手动在命令行启动的命令是一样的
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;autostart &lt;span style="color:#f92672"&gt;=&lt;/span&gt; true ; 在 supervisord 启动的时候也自动启动
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;startsecs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; ; 启动 &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 秒后没有异常退出，就当作已经正常启动了
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;autorestart &lt;span style="color:#f92672"&gt;=&lt;/span&gt; true ; 程序异常退出后自动重启
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;startretries &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; ; 启动失败自动重试次数，默认是 &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; durban ; 用哪个用户启动
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;redirect_stderr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; true ; 把 stderr 重定向到 stdout，默认 false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stdout_logfile_maxbytes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 20MB ; stdout 日志文件大小，默认 50MB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stdout_logfile_backups &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt; ; stdout 日志文件备份数
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; stdout 日志文件，需要注意当指定目录不存在时无法正常启动，所以需要手动创建目录（supervisord 会自动创建日志文件）
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stdout_logfile &lt;span style="color:#f92672"&gt;=&lt;/span&gt; /var/tmp/baby_stdout.log
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; 可以通过 environment 来添加需要的环境变量，一种常见的用法是修改 PYTHONPATH
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; environment&lt;span style="color:#f92672"&gt;=&lt;/span&gt;PYTHONPATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$PYTHONPATH:/path/to/somewhere
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后重启下supervisor就可以了&lt;/p&gt;
&lt;p&gt;这里注意下supervisor的问题，如果你supervisor的配置文件是在/etc/等supervisor默认搜索的目录下的话直接使用supervisorctl reload是没有任何问题的，如果配置文件不在默认的搜索目录下的话，就会报类似refused connection的错误提示，建议操作supervisor的时候，指定下配置文件，如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;supervisorctl -c /usr/local/etc/supervisord.ini reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后配置nginx，简单的配置示例如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	charset utf-8;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	client_max_body_size 128M;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	listen 80;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	server_name flask1.walkerfree.local; &lt;span style="color:#75715e"&gt;# 这是HOST机器的外部域名，用地址也行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	access_log /var/log/flask1.walkerfree.local.access.log;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	error_log /var/log/flask1.walkerfree.local.error.log;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	location / &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		proxy_pass http://127.0.0.1:8000; &lt;span style="color:#75715e"&gt;# 这里是指向 gunicorn host 的服务地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		proxy_set_header Host $host;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	location ~ /&lt;span style="color:#ae81ff"&gt;\.&lt;/span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;git|svn|ht&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		deny all;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask 日志邮件发送 客户端授权密码验证错误</title><link>https://zhangdapeng.dev/p/flask-ri-zhi-you-jian-fa-song-ke-hu-duan-shou-quan-mi-ma-yan-zheng-cuo-wu/</link><pubDate>Mon, 28 Jul 2025 17:52:42 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-ri-zhi-you-jian-fa-song-ke-hu-duan-shou-quan-mi-ma-yan-zheng-cuo-wu/</guid><description>&lt;img src="https://res.cloudinary.com/dy5dvcuc1/image/upload/c_scale,w_520/v1530152883/walkerfree/stockvault-feet-of-an-athlete-running-on-a-deserted-road179154.jpg" alt="Featured image of post Flask 日志邮件发送 客户端授权密码验证错误" /&gt;&lt;p&gt;最近在使用flask的日志邮件发送功能，就是程序出现问题的时候，可以通过邮件发送到指定的邮箱，当时使用的是QQ邮箱的&lt;/p&gt;
&lt;p&gt;基本上会有如下的错误&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;smtplib.SMTPAuthenticationError: (535, b&amp;rsquo;Error: authentication failed')&lt;/p&gt;
&lt;p&gt;smtplib.SMTPServerDisconnected: Connection unexpectedly closed&lt;/p&gt;
&lt;p&gt;Connection unexpectedly closed: timed out&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;开始的配置如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ADMINS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;xxx@xxx&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_SUBJECT_PREFIX &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;[Walkerfree]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_SENDER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;xxx@xxx&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_SERVER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;smtp.qq.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_PORT &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;465&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_USE_SSL &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_USE_TLS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_USERNAME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;xxx@xxx&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_PASSWORD &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;123456&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在测试的时候会遇到超时，但是是这里的问题&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mail_handler &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SMTPHandler(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_SERVER&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_SENDER&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;ADMINS&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;application error&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_USERNAME&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_PASSWORD&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数没有传递端口号，可以看源码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mailhost, self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mailport &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mailhost
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有mailhost和mailport，没有端口号怎么能行呢&lt;/p&gt;
&lt;p&gt;修改后如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mail_handler &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SMTPHandler(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_SERVER&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_PORT&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_SENDER&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;ADMINS&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;application error&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_USERNAME&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;MAIL_PASSWORD&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;报错如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SMTPServerDisconnected: Connection unexpectedly closed: timed out
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后修改了配置，如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ADMINS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;xxx@xxx&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_SUBJECT_PREFIX &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;[Walkerfree]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_SENDER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;xxx@xxx&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_SERVER &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;smtp.qq.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_PORT &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_USE_SSL &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_USE_TLS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_USERNAME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;xxx@xxx&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAIL_PASSWORD &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;123456&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后发送成功。客户端授权码的这个需要自己去搞，QQ帮助中心有指引的。&lt;/p&gt;
&lt;p&gt;快捷链接:http://service.mail.qq.com/cgi-bin/help?subtype=1&amp;amp;&amp;amp;id=28&amp;amp;&amp;amp;no=1001256%27)&lt;/p&gt;</description></item><item><title>Flask 之 Gunicorn启动应用程序</title><link>https://zhangdapeng.dev/p/flask-zhi-gunicorn-qi-dong-ying-yong-cheng-xu/</link><pubDate>Tue, 22 Jul 2025 18:31:35 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-zhi-gunicorn-qi-dong-ying-yong-cheng-xu/</guid><description>&lt;p&gt;Gunicorn的介绍我就不锁了，但是我这里简单记录一下我自己是如何使用Gunicorn的。&lt;/p&gt;
&lt;p&gt;首先安装Gunicorn&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install gunicorn
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后添加一下gunicorn的启动文件-gunicorn.conf&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;workers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bind &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1:3041&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;daemon &lt;span style="color:#f92672"&gt;=&lt;/span&gt; True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;accesslog &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;./walkerfree/logs/access.log&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;errorlog &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;./walkerfree/logs/error.log&amp;#39;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的log路径可以根据自己的情况进行修改，bind的内容也是一样的&lt;/p&gt;
&lt;p&gt;最后启动自己的web应用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gunicorn wsgi:application -c gunicorn.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到这里会有一个疑问是：wsgi:application，是这样的，首先创建一个wsgi文件，是的就是这个文件，这个跟python的包有关&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# _*_ coding=utf-8 _*_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree.config &lt;span style="color:#f92672"&gt;import&lt;/span&gt; config
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; bootstrap
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; mail
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; moment
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; walkerfree &lt;span style="color:#f92672"&gt;import&lt;/span&gt; dispatch_handlers, register_blueprints, register_jinja_env, configure_assets, configure_logging
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_app&lt;/span&gt;(config_name):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_object(config[config_name])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config[config_name]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bootstrap&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mail&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; moment&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cache&lt;span style="color:#f92672"&gt;.&lt;/span&gt;init_app(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dispatch_handlers(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; register_blueprints(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; register_jinja_env(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configure_logging(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configure_assets(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;application &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_app(&lt;span style="color:#e6db74"&gt;&amp;#39;production&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run() 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我这里的代码，是根据自己的情况定义的。&lt;/p&gt;</description></item><item><title>Flask之更新表结构</title><link>https://zhangdapeng.dev/p/flask-zhi-geng-xin-biao-jie-gou/</link><pubDate>Tue, 22 Jul 2025 18:31:31 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-zhi-geng-xin-biao-jie-gou/</guid><description>&lt;p&gt;使用Flask的过程中，对于操作数据库这一块会用到Flask-SqlAlchemy,项目开始的时候是没有什么问题的，但是在后面由于业务需求会涉及到修改表的操作，那么问题来了，表的结构被修改之后，又如何才能同步到生产环境的数据库呢。&lt;/p&gt;
&lt;p&gt;这里推荐使用一个Flask的插件-Flask-Migrage.下面掩饰一下如何去操作。&lt;/p&gt;
&lt;p&gt;Flask-Migrate的安装：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install flask-migrate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建app.py，代码如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Flask
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.ext.sqlalchemy &lt;span style="color:#f92672"&gt;import&lt;/span&gt; SQLAlchemy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.ext.script &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Manager
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; flask.ext.migrate &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Migrate, MigrateCommand
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flask(__name__)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;SQLALCHEMY_DATABASE_URI&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;sqlite:///app.db&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SQLAlchemy(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;migrate &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Migrate(app, db)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;manager &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Manager(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_command(&lt;span style="color:#e6db74"&gt;&amp;#39;db&amp;#39;&lt;/span&gt;, MigrateCommand)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Integer, primary_key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Column(db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;String(&lt;span style="color:#ae81ff"&gt;128&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run() 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;保存如何代码后，实行如下代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python app.py db init 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个动作是说，初始化migrate需要的环境，接下来生成一个数据表操作的代码文件：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python app.py db migrate 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果没有报错的话，会出现一系列的提示信息，这里具体情况不一样，我就不一列出来了。&lt;/p&gt;
&lt;p&gt;如果上面执行后没有问题的话，就可以进行数据库表的更新了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python app.py db upgrade 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行上面的代码，就可以更新数据库表的结构了。&lt;/p&gt;</description></item><item><title>Flask SqlAlchemy Like 查询</title><link>https://zhangdapeng.dev/p/flask-sqlalchemy-like-cha-xun/</link><pubDate>Tue, 22 Jul 2025 18:31:27 +0800</pubDate><guid>https://zhangdapeng.dev/p/flask-sqlalchemy-like-cha-xun/</guid><description>&lt;p&gt;SQLAlchemy 如何进行 mysql的Like查询呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Article&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(Category)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(Article&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title&lt;span style="color:#f92672"&gt;.&lt;/span&gt;like(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%%&lt;/span&gt;&lt;span style="color:#e6db74"&gt;s%&amp;#34;&lt;/span&gt;, name), Article&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_deleted &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;order_by(Article&lt;span style="color:#f92672"&gt;.&lt;/span&gt;id&lt;span style="color:#f92672"&gt;.&lt;/span&gt;desc())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>supervisor + uwsgi + flask</title><link>https://zhangdapeng.dev/p/supervisor-uwsgi-flask/</link><pubDate>Fri, 04 Jul 2025 11:48:04 +0000</pubDate><guid>https://zhangdapeng.dev/p/supervisor-uwsgi-flask/</guid><description>&lt;p&gt;supervisor启动flask项目中，配置了uwsgi，即通过supervisor启动uwsgi，uwsgi再启动flask项目&lt;br&gt;
这个过程中uwsgi是个坑&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;uwsgi&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;chdir&amp;gt;&lt;/span&gt;/relativeProjectPath&lt;span style="color:#f92672"&gt;&amp;lt;/chdir&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;wsgi_handler&lt;span style="color:#f92672"&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;socket&amp;gt;&lt;/span&gt;/tmp/walkerfree-uwsgi.sock&lt;span style="color:#f92672"&gt;&amp;lt;/socket&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;daemonize&amp;gt;&lt;/span&gt;/tmp/uwsgi.walkerfree.log&lt;span style="color:#f92672"&gt;&amp;lt;/daemonize&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;master&amp;gt;&lt;/span&gt;4&lt;span style="color:#f92672"&gt;&amp;lt;/master&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;chmod-socket&amp;gt;&lt;/span&gt;666&lt;span style="color:#f92672"&gt;&amp;lt;/chmod-socket&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;enable-threads&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;processes&amp;gt;&lt;/span&gt;4&lt;span style="color:#f92672"&gt;&amp;lt;/processes&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;harakiri&amp;gt;&lt;/span&gt;30&lt;span style="color:#f92672"&gt;&amp;lt;/harakiri&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;workers&amp;gt;&lt;/span&gt;6&lt;span style="color:#f92672"&gt;&amp;lt;/workers&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;memory-report&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;vacuum/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/uwsgi&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个是我的初始配置文件，如果单独使用uwsgi启动是没有问题的，但是放在supervisor就有问题，会启动很多个uwsgi进程，而且supersior也会报错&lt;br&gt;
我见到的错误如下几种&lt;br&gt;
1】ERROR (spawn error)&lt;br&gt;
2】BACKOFF Exited too quickly (process log may have details)&lt;/p&gt;
&lt;p&gt;先给下最终的uwsgi配置方案,如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;uwsgi&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;chdir&amp;gt;&lt;/span&gt;relativeProjectPath&lt;span style="color:#f92672"&gt;&amp;lt;/chdir&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;wsgi_handler&lt;span style="color:#f92672"&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;socket&amp;gt;&lt;/span&gt;/tmp/walkerfree_uwsgi.sock&lt;span style="color:#f92672"&gt;&amp;lt;/socket&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;logto&amp;gt;&lt;/span&gt;/tmp/walkerfree_uwsgi.log&lt;span style="color:#f92672"&gt;&amp;lt;/logto&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;chmod-socket&amp;gt;&lt;/span&gt;666&lt;span style="color:#f92672"&gt;&amp;lt;/chmod-socket&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;enable-threads&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;processes&amp;gt;&lt;/span&gt;1&lt;span style="color:#f92672"&gt;&amp;lt;/processes&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;harakiri&amp;gt;&lt;/span&gt;30&lt;span style="color:#f92672"&gt;&amp;lt;/harakiri&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;memory-report&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;vacuum/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/uwsgi&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;具体有几个坑吧，我试了两种，具体有一下几个方法&lt;br&gt;
第一个:&lt;br&gt;
&lt;strong&gt;修改daemonize为logto&lt;/strong&gt;&lt;br&gt;
第二个:&lt;br&gt;
去掉master【这项可以不用去掉，如果不起作用可以试试】&lt;br&gt;
去掉workers【这项可以不用去掉，如果不起作用可以试试】&lt;br&gt;
修改processes为1&lt;/p&gt;
&lt;p&gt;supervisor中项目的配置如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[program:walkerfree]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# http://supervisord.org/configuration.html#program-x-section-example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;command&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/projectPath/env/bin/uwsgi -x /projectPath/uwsgi.xml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;autostart&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;autorestart&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;stdout_logfile&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/projectPath/supervisor/out.log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;stderr_logfile&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/projectPath/supervisor/err.log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>2015-09-10 工作记录</title><link>https://zhangdapeng.dev/p/2015-09-10-gong-zuo-ji-lu/</link><pubDate>Tue, 01 Jul 2025 15:04:20 +0000</pubDate><guid>https://zhangdapeng.dev/p/2015-09-10-gong-zuo-ji-lu/</guid><description>&lt;p&gt;1、查看innodb死锁情况&lt;/p&gt;
&lt;p&gt;查看事务情况&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;show engine innodb status&lt;span style="color:#ae81ff"&gt;\G&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看事务详情&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; information_schema.INNODB_TRX&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;G&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;2、kibana 4 添加图表，以及如何在dashboard【仪表板】上展示所有的图表或者分开展示图表&lt;/p&gt;
&lt;p&gt;添加图表的流程一定要清楚，先要有visualization【可视化视图】，然后才能在dashboard【仪表板】上面添加图表，事实上图表就是所谓的&lt;/p&gt;
&lt;p&gt;visualization【可视化视图】；&lt;/p&gt;
&lt;p&gt;这里的操作关键比较难的是如何添加visualization【可视化视图】，并且在添加visualization【可视化视图】过程中的Filter的操作。&lt;/p&gt;
&lt;p&gt;Filter说起来也很简单就是搜索出你要展示的数据数量的值，比如访问链接地址的时长，某个链接的请求次数的值。&lt;/p&gt;
&lt;p&gt;关键是在添加visualization【可视化视图】过程中，先要选择视图的类型，然后就要对metrics和buckets的操作。&lt;/p&gt;
&lt;p&gt;这里有一篇关于kibana4的图表入门：&lt;a class="link" href="https://www.timroes.de/2015/02/07/kibana-4-tutorial-part-3-visualize/" target="_blank" rel="noopener"
 &gt;请点击这里&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;3、curl 指定用户以及密码访问&lt;/p&gt;
&lt;p&gt;很简单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -u user:password -XGET http://www.xxx.net/study/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就可以了.&lt;/p&gt;
&lt;p&gt;4、通过python的线程实现异步发送邮件&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; threading &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Thread
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;send_async_email&lt;/span&gt;(app, msg):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;app_context():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mail&lt;span style="color:#f92672"&gt;.&lt;/span&gt;send(msg)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;send_email&lt;/span&gt;(to, subject, template, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; msg &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Message(app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;FLASKY_MAIL_SUBJECT_PREFIX&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; subject,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sender&lt;span style="color:#f92672"&gt;=&lt;/span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;config[&lt;span style="color:#e6db74"&gt;&amp;#39;FLASKY_MAIL_SENDER&amp;#39;&lt;/span&gt;], recipients&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[to])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; msg&lt;span style="color:#f92672"&gt;.&lt;/span&gt;body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; render_template(template &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;.txt&amp;#39;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; msg&lt;span style="color:#f92672"&gt;.&lt;/span&gt;html &lt;span style="color:#f92672"&gt;=&lt;/span&gt; render_template(template &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;.html&amp;#39;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; thr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Thread(target&lt;span style="color:#f92672"&gt;=&lt;/span&gt;send_async_email, args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[app, msg])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; thr&lt;span style="color:#f92672"&gt;.&lt;/span&gt;start()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; thr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;5、针对Flask项目初始化数据库&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; from walkerfree import config
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; from walkerfree import app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; app.config.from_object&lt;span style="color:#f92672"&gt;(&lt;/span&gt;config&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;development&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; from walkerfree import db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; db.init_app&lt;span style="color:#f92672"&gt;(&lt;/span&gt;app&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;SQLAlchemy engine&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;mysql+mysqldb://root:@127.0.0.1/walkerfree?charset=utf8&amp;#39;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; db.create_all&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Flask + uwsgi 在Nginx服务器上部署</title><link>https://zhangdapeng.dev/p/flask-uwsgi-zai-nginx-fu-wu-qi-shang-bu-shu/</link><pubDate>Mon, 30 Jun 2025 14:30:51 +0000</pubDate><guid>https://zhangdapeng.dev/p/flask-uwsgi-zai-nginx-fu-wu-qi-shang-bu-shu/</guid><description>&lt;p&gt;Nginx配置如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; listen 80; &lt;span style="color:#75715e"&gt;## listen for ipv4; this line is default and implied&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;#listen [::]:80 default ipv6only=on; ## listen for ipv6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; server_name flask.blog.gowhich.dev;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; location / &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; include uwsgi_params;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uwsgi_pass 0.0.0.0:3032;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;uwsgi 【uwsgi.xml】配置如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;uwsgi&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;chdir&amp;gt;/xxx/xxx/python/python_project/flask_blog_small_1&amp;lt;/chdir&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;module&amp;gt;flask_blog&amp;lt;/module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;callable&amp;gt;app&amp;lt;/callable&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;socket&amp;gt;0.0.0.0:3032&amp;lt;/socket&amp;gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;daemonize&amp;gt;/var/log/uwsgi.flask.blog.log&amp;lt;/daemonize&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;master/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;processes&amp;gt;4&amp;lt;/processes&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;memory-report/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/uwsgi&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启动uwsgi和nginx便可完成部署;&lt;/p&gt;
&lt;p&gt;这里的端口号只要保证与uwsgi的端口号一直就可以，不用考虑flask自己运行的脚本&lt;/p&gt;</description></item><item><title>Flask中实现使用Mongodb分页的程序逻辑</title><link>https://zhangdapeng.dev/p/flask-zhong-shi-xian-shi-yong-mongodb-fen-ye-de-cheng-xu-luo-ji/</link><pubDate>Mon, 30 Jun 2025 14:09:00 +0000</pubDate><guid>https://zhangdapeng.dev/p/flask-zhong-shi-xian-shi-yong-mongodb-fen-ye-de-cheng-xu-luo-ji/</guid><description>&lt;p&gt;实现的逻辑如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;)) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pagesize &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prev_page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; page &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; page &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; page &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 关键字查询&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keywords &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;keywords&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start_date &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;start_date&amp;#39;&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end_date &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;end_date&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; keywords:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append({&lt;span style="color:#e6db74"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;:{&lt;span style="color:#e6db74"&gt;&amp;#39;$regex&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;^&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt;keywords }})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query[&lt;span style="color:#e6db74"&gt;&amp;#39;keywords&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; keywords
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; start_date &lt;span style="color:#f92672"&gt;and&lt;/span&gt; end_date:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 00:00:00&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; start_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 23:59:59&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; end_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mktime(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strptime(start,&lt;span style="color:#e6db74"&gt;&amp;#39;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#39;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mktime(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strptime(end,&lt;span style="color:#e6db74"&gt;&amp;#39;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#39;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append({&lt;span style="color:#e6db74"&gt;&amp;#39;create_time&amp;#39;&lt;/span&gt;:{&lt;span style="color:#e6db74"&gt;&amp;#39;$gt&amp;#39;&lt;/span&gt;:start}})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append({&lt;span style="color:#e6db74"&gt;&amp;#39;create_time&amp;#39;&lt;/span&gt;:{&lt;span style="color:#e6db74"&gt;&amp;#39;$lte&amp;#39;&lt;/span&gt;:end}})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query[&lt;span style="color:#e6db74"&gt;&amp;#39;start_date&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; start_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query[&lt;span style="color:#e6db74"&gt;&amp;#39;end_date&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; end_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; params:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# if len(params) == 1:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# where = params&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# else:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; where &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;$and&amp;#39;&lt;/span&gt;:params} 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes_sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mongo&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(where)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mongo&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(where)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;skip((page &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; pagesize)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort(&lt;span style="color:#e6db74"&gt;&amp;#39;create_time&amp;#39;&lt;/span&gt;,&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;limit(pagesize)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes_sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mongo&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mongo&lt;span style="color:#f92672"&gt;.&lt;/span&gt;db&lt;span style="color:#f92672"&gt;.&lt;/span&gt;itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;skip((page &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; pagesize)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort(&lt;span style="color:#e6db74"&gt;&amp;#39;create_time&amp;#39;&lt;/span&gt;,&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;limit(pagesize)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;front/spider_itunes.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;itunes,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes_sum&lt;span style="color:#f92672"&gt;=&lt;/span&gt;itunes_sum,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;u&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;iTunes爬虫&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prev_page&lt;span style="color:#f92672"&gt;=&lt;/span&gt;prev_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_page&lt;span style="color:#f92672"&gt;=&lt;/span&gt;next_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params&lt;span style="color:#f92672"&gt;=&lt;/span&gt;params_query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;mongodb还是别扭点，常用就好了。&lt;/p&gt;</description></item><item><title>Flask中实现使用SQLAlchemy MySQLdb分页的程序逻辑</title><link>https://zhangdapeng.dev/p/flask-zhong-shi-xian-shi-yong-sqlalchemy-mysqldb-fen-ye-de-cheng-xu-luo-ji/</link><pubDate>Mon, 30 Jun 2025 14:08:58 +0000</pubDate><guid>https://zhangdapeng.dev/p/flask-zhong-shi-xian-shi-yong-sqlalchemy-mysqldb-fen-ye-de-cheng-xu-luo-ji/</guid><description>&lt;p&gt;逻辑的话还是蛮简单的，多查看一个文档就可以了。代码如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;)) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pagesize &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prev_page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; page &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; page &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; page &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 关键字查询&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keywords &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;keywords&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start_date &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;start_date&amp;#39;&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end_date &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;end_date&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; keywords:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(Itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;title&lt;span style="color:#f92672"&gt;.&lt;/span&gt;startswith(keywords))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query[&lt;span style="color:#e6db74"&gt;&amp;#39;keywords&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; keywords
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; start_date &lt;span style="color:#f92672"&gt;and&lt;/span&gt; end_date:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 00:00:00&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; start_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 23:59:59&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; end_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mktime(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strptime(start,&lt;span style="color:#e6db74"&gt;&amp;#39;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#39;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mktime(time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strptime(end,&lt;span style="color:#e6db74"&gt;&amp;#39;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#39;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(Itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;create_time &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; start)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(Itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;create_time &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; end)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query[&lt;span style="color:#e6db74"&gt;&amp;#39;start_date&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; start_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params_query[&lt;span style="color:#e6db74"&gt;&amp;#39;end_date&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; end_date
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; params_query: 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes_sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;offset((page&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;*&lt;/span&gt;pagesize)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;limit(pagesize)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes_sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Itunes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;query&lt;span style="color:#f92672"&gt;.&lt;/span&gt;offset((page&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;*&lt;/span&gt;pagesize)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;limit(pagesize)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; render_template(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;front/spider_apple.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;itunes,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itunes_sum&lt;span style="color:#f92672"&gt;=&lt;/span&gt;itunes_sum,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;u&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Apple爬虫&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prev_page&lt;span style="color:#f92672"&gt;=&lt;/span&gt;prev_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_page&lt;span style="color:#f92672"&gt;=&lt;/span&gt;next_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; params&lt;span style="color:#f92672"&gt;=&lt;/span&gt;params_query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item></channel></rss>