Flask和@app.route(1)

本文详细介绍了Flask中app.route()装饰器的工作原理,包括如何使用装饰器工厂实现自定义路由装饰器,以及如何通过字典存储路径与视图函数之间的关联。同时,通过单元测试验证了实现的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文我们先来说说Flask,深入探讨Flask如何实现在函数上方写“@app.route()”就能在因特网上输出函数的执行结果。

下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的。
<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">app = Flask(__name__)

<span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@app.route("/")</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">hello</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Hello World!"</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

@app.route和其它装饰器

要想明白“@app.route()”的工作原理,我们首先需要看一看Python中的装饰器(就是以“@”开头的那玩意,下面接着函数定义)。

究竟什么是装饰器?没啥特别的。装饰器只是一种接受函数(就是那个你用“@”符号装饰的函数)的函数,并返回一个新的函数。

当你装饰一个函数,意味着你告诉Python调用的是那个由你的装饰器返回的新函数,而不仅仅是直接返回原函数体的执行结果。

还不是很明白?这里是一个简单的例子:

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># This is our decorator</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">simple_decorator</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(f)</span>:</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># This is the new function we're going to return</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># This function will be used in place of our original definition</span>
    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">wrapper</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Entering Function"</span>
        f()
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Exited Function"</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> wrapper

<span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@simple_decorator </span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">hello</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Hello World"</span>

hello()</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

运行上述代码会输出以下结果:

Entering Function 
Hello World 
Exited Function

很好!

现在我们有点明白怎样创建我们自己的“@app.route()”装饰器了,但你可能会注意到有一个不同点,就是我们的simple_decorator不可以接受任何参数, 但“@app.route()”却可以。

那么我们怎样才能给我们的装饰器传参数?要实现这个我们只需创建一个“decorator_factory”函数,我们调用这个函数,返回适用于我们函数的装饰器。现在看看如果实现它。

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">decorator_factory</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(enter_message, exit_message)</span>:</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># We're going to return this decorator</span>
    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">simple_decorator</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(f)</span>:</span>
        <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">wrapper</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> enter_message
            f()
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> exit_message

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> wrapper

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> simple_decorator

<span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@decorator_factory("Start", "End")</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">hello</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Hello World"</span>

hello()</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

给我们的输出是:

Start 
Hello World 
End 
请注意在我们写@decorator_factory(“Start”, “End”)时,我们实际调用的是decorator_factory函数,实际返回的装饰器已经被用上了,代码很整洁,对吧?

把“app”放进“app.route”

现在我们掌握了装饰器怎样工作的全部前置知识 ,可以重新实现Flask API的这个部分了,那么把我们的目光转移到“app”在我们Flask应用中的重要地位上面来。

在开始解释Flask对象里面发生了什么之前,我们先创建我们自己的Python类NotFlask。

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NotFlask</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">pass</span>

app = NotFlask()</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

这不是个很有趣的类,不过有一样值得注意,就是这个类的方法也可以被用作装饰器,所以让我们把这个类写得更有趣一点,加一个称作 route的方法,它是一个简单的装饰器工厂。

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NotFlask</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">route</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, route_str)</span>:</span>
        <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">decorator</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(f)</span>:</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> f

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> decorator

app = NotFlask()

<span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@app.route("/")</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">hello</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Hello World!"</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

这个装饰器和我们之前创建的那些最大的不同,在于我们不想修改被我们装饰的函数的行为,我们只是想获得它的引用。

所以,最后一步是我们打算去利用一个特性,就是用装饰器函数的副产品去保存一个提供给我们的路径之间的链接,装饰器函数应该与它关联起来。

为了实现这个,我们给我们的NotFlask对象加一个“routes”字典,当我们的“decorator”函数被调用,路径将被插入新字典中函数对应的位置。

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NotFlask</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">__init__</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self)</span>:</span>
        self.routes = {}

    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">route</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, route_str)</span>:</span>
        <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">decorator</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(f)</span>:</span>
            self.routes[route_str] = f
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> f

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> decorator

app = NotFlask()

<span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@app.route("/")</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">hello</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Hello World!"</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

现在我们就要完成了!可如果没法访问内部的视图函数,保存路径的字典又有什么用?让我们加入一个方法serve(path),当给定的路径存在时运行一个函数并给们我结果,当路径尚未注册时则抛出一个异常。

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NotFlask</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">__init__</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self)</span>:</span>
        self.routes = {}

    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">route</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, route_str)</span>:</span>
        <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">decorator</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(f)</span>:</span>
            self.routes[route_str] = f
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> f

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> decorator

    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">serve</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, path)</span>:</span>
        view_function = self.routes.get(path)
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> view_function:
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> view_function()
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>:
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">raise</span> ValueError(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Route "{}"" has not been registered'</span>.format(path))

app = NotFlask()

<span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@app.route("/")</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">hello</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Hello World!"</span>

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span> app.serve(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/"</span>)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>

我们会看到:

Hello World!

我们已经完成了一个的Flask网页上第一个例子的非常简单的重现,让我们写一些快速测试检测我们简单重现的Flask的“@app.route()”是否正确。

<code class="hljs python has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#-*- coding: UTF-8 -*-</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> unittest


<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">TestNotFlask</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(unittest.TestCase)</span>:</span>
    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">setUp</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self)</span>:</span>
        self.app = NotFlask()

    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">test_valid_route</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self)</span>:</span>
        <span class="hljs-decorator" style="color: rgb(0, 102, 102); box-sizing: border-box;">@self.app.route('/')</span>
        <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">index</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Hello World'</span>

        self.assertEqual(self.app.serve(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'/'</span>), <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Hello World'</span>)

    <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">test_invalid_route</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self)</span>:</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">with</span> self.assertRaises(ValueError):
            self.app.serve(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'/invalid'</span>)

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> __name__ == <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'__main__'</span>:  
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># unittest.main() # 用这个是最简单的,下面的用法可以同时测试多个类  </span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># unittest.TextTestRunner(verbosity=2).run(suite1) # 这个等价于上述但可设置verbosity=2,省去了运行时加-v  </span>
    suite1 = unittest.TestLoader().loadTestsFromTestCase(TestNotFlask)  
    suite2 = unittest.TestLoader().loadTestsFromTestCase(TestNotFlask)  
    suite = unittest.TestSuite([suite1, suite2])  
    unittest.TextTestRunner(verbosity=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>).run(suite)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>

输出

Hello World! 
test_invalid_route (main.TestNotFlask) … ok 
test_valid_route (main.TestNotFlask) … ok 
test_invalid_route (main.TestNotFlask) … ok 
test_valid_route (main.TestNotFlask) … ok


Ran 4 tests in 0.003s

OK

完全正确!所以,仅仅是一个简单的包含一个字典的装饰器, 就重现了Flask的“app.route()”装饰器的基本的行为。

在本系列的下一篇,也是Flask的app.route()的最后一篇,将通过解析下面这个例子来解释动态URL模式是如何工作。

原文地址:http://python.jobbole.com/80956/

from flask import Flask, render_template, request, redirect, url_for, flash from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///shop.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login' # 用户模型 class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password_hash = db.Column(db.String(128)) orders = db.relationship('Order', backref='user', lazy=True) cart_items = db.relationship('CartItem', backref='user', lazy=True) def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) # 商品模型 class Product(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) description = db.Column(db.Text) price = db.Column(db.Float, nullable=False) stock = db.Column(db.Integer, nullable=False) image_url = db.Column(db.String(200)) # 订单模型 class Order(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) date_ordered = db.Column(db.DateTime, default=datetime.utcnow) status = db.Column(db.String(20), default='pending') # pending, completed, refunded items = db.relationship('OrderItem', backref='order', lazy=True) total_amount = db.Column(db.Float, nullable=False) def can_delete(self): """判断订单是否可以删除""" return self.status in ['pending', 'cancelled'] def can_deliver(self): """判断订单是否可以交付""" return self.status == 'pending' # 订单项模型 class OrderItem(db.Model): id = db.Column(db.Integer, primary_key=True) order_id = db.Column(db.Integer, db.ForeignKey('order.id'), nullable=False) product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False) quantity = db.Column(db.Integer, nullable=False) price = db.Column(db.Float, nullable=False) product = db.relationship('Product') # 购物车项模型 class CartItem(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False) quantity = db.Column(db.Integer, nullable=False) product = db.relationship('Product') @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) # 首页 - 商品展示 @app.route('/') def index(): products = Product.query.all() return render_template('index.html', products=products) # 用户个人信息 @app.route('/profile') @login_required def profile(): return render_template('profile.html') # 购物车 @app.route('/cart') @login_required def cart(): cart_items = CartItem.query.filter_by(user_id=current_user.id).all() total = sum(item.product.price * item.quantity for item in cart_items) return render_template('cart.html', cart_items=cart_items, total=total) # 订单历史 @app.route('/orders') @login_required def orders(): user_orders = Order.query.filter_by(user_id=current_user.id).all() return render_template('orders.html', orders=user_orders) # 添加到购物车 @app.route('/add_to_cart/<int:product_id>', methods=['POST']) @login_required def add_to_cart(product_id): quantity = int(request.form.get('quantity', 1)) cart_item = CartItem.query.filter_by(user_id=current_user.id, product_id=product_id).first() if cart_item: cart_item.quantity += quantity else: cart_item = CartItem(user_id=current_user.id, product_id=product_id, quantity=quantity) db.session.add(cart_item) db.session.commit() flash('商品已添加到购物车!') return redirect(url_for('cart')) # 创建订单 @app.route('/create_order', methods=['POST']) @login_required def create_order(): cart_items = CartItem.query.filter_by(user_id=current_user.id).all() if not cart_items: flash('购物车为空!') return redirect(url_for('cart')) total_amount = sum(item.product.price * item.quantity for item in cart_items) order = Order(user_id=current_user.id, total_amount=total_amount) db.session.add(order) for cart_item in cart_items: order_item = OrderItem( order=order, product_id=cart_item.product_id, quantity=cart_item.quantity, price=cart_item.product.price ) db.session.add(order_item) # 更新库存 product = cart_item.product product.stock -= cart_item.quantity db.session.delete(cart_item) db.session.commit() flash('订单创建成功!') return redirect(url_for('orders')) # 申请退款 @app.route('/refund/<int:order_id>', methods=['POST']) @login_required def refund_order(order_id): order = Order.query.get_or_404(order_id) if order.user_id != current_user.id: flash('无权限操作此订单!') return redirect(url_for('orders')) if order.status == 'completed': order.status = 'refunded' # 恢复库存 for item in order.items: product = item.product product.stock += item.quantity db.session.commit() flash('退款申请已提交!') else: flash('订单状态不允许退款!') return redirect(url_for('orders')) # 用户认证相关路由 @app.route('/login', methods=['GET', 'POST']) def login(): if current_user.is_authenticated: return redirect(url_for('index')) if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') remember = 'remember' in request.form user = User.query.filter_by(username=username).first() if user and user.check_password(password): login_user(user, remember=remember) next_page = request.args.get('next') return redirect(next_page or url_for('index')) flash('用户名或密码错误') return render_template('login.html') @app.route('/register', methods=['GET', 'POST']) def register(): if current_user.is_authenticated: return redirect(url_for('index')) if request.method == 'POST': username = request.form.get('username') email = request.form.get('email') password = request.form.get('password') confirm_password = request.form.get('confirm_password') if password != confirm_password: flash('两次输入的密码不一致') return redirect(url_for('register')) if User.query.filter_by(username=username).first(): flash('用户名已存在') return redirect(url_for('register')) if User.query.filter_by(email=email).first(): flash('邮箱已被注册') return redirect(url_for('register')) user = User(username=username, email=email) user.set_password(password) db.session.add(user) db.session.commit() flash('注册成功,请登录') return redirect(url_for('login')) return render_template('register.html') @app.route('/logout') @login_required def logout(): logout_user() return redirect(url_for('index')) @app.route('/update_profile', methods=['POST']) @login_required def update_profile(): email = request.form.get('email') if email != current_user.email: if User.query.filter_by(email=email).first(): flash('邮箱已被使用') else: current_user.email = email db.session.commit() flash('个人信息更新成功') return redirect(url_for('profile')) @app.route('/change_password', methods=['POST']) @login_required def change_password(): current_password = request.form.get('current_password') new_password = request.form.get('new_password') confirm_password = request.form.get('confirm_password') if not current_user.check_password(current_password): flash('当前密码错误') elif new_password != confirm_password: flash('两次输入的新密码不一致') else: current_user.set_password(new_password) db.session.commit() flash('密码修改成功') return redirect(url_for('profile')) # 购物车相关路由 @app.route('/update_cart_item/<int:item_id>', methods=['POST']) @login_required def update_cart_item(item_id): cart_item = CartItem.query.get_or_404(item_id) if cart_item.user_id != current_user.id: flash('无权限操作此购物车项') return redirect(url_for('cart')) quantity = int(request.form.get('quantity', 1)) if quantity > cart_item.product.stock: flash('商品库存不足') else: cart_item.quantity = quantity db.session.commit() flash('购物车更新成功') return redirect(url_for('cart')) @app.route('/remove_from_cart/<int:item_id>', methods=['POST']) @login_required def remove_from_cart(item_id): cart_item = CartItem.query.get_or_404(item_id) if cart_item.user_id != current_user.id: flash('无权限操作此购物车项') return redirect(url_for('cart')) db.session.delete(cart_item) db.session.commit() flash('商品已从购物车中移除') return redirect(url_for('cart')) # 添加删除订单功能 @app.route('/cancel_order/<int:order_id>', methods=['POST']) @login_required def cancel_order(order_id): order = Order.query.get_or_404(order_id) # 验证订单属于当前用户 if order.user_id != current_user.id: flash('无权限操作此订单', 'danger') return redirect(url_for('orders')) # 检查订单状态是否可以取消 if not order.can_delete(): flash('订单当前状态不可取消', 'danger') return redirect(url_for('orders')) try: # 恢复库存 for item in order.items: product = item.product product.stock += item.quantity # 更新订单状态为已取消 order.status = 'cancelled' db.session.commit() flash('订单已成功取消', 'success') except Exception as e: db.session.rollback() flash('取消订单时出错: {}'.format(str(e)), 'danger') return redirect(url_for('orders')) # 添加管理员端订单交付功能 @app.route('/admin/deliver_order/<int:order_id>', methods=['POST']) @login_required def deliver_order(order_id): # 检查是否是管理员 if not current_user.is_authenticated or current_user.username != 'admin': flash('需要管理员权限', 'danger') return redirect(url_for('index')) order = Order.query.get_or_404(order_id) # 检查订单状态是否可以交付 if not order.can_deliver(): flash('订单当前状态不可交付', 'danger') return redirect(url_for('admin_orders')) try: # 更新订单状态为已完成 order.status = 'completed' db.session.commit() flash('订单已标记为已交付', 'success') except Exception as e: db.session.rollback() flash('交付订单时出错: {}'.format(str(e)), 'danger') return redirect(url_for('admin_orders')) # 添加管理员端订单删除功能 @app.route('/admin/delete_order/<int:order_id>', methods=['POST']) @login_required def admin_delete_order(order_id): # 检查是否是管理员 if not current_user.is_authenticated or current_user.username != 'admin': flash('需要管理员权限', 'danger') return redirect(url_for('index')) order = Order.query.get_or_404(order_id) # 检查订单状态是否可以删除 if not order.can_delete(): flash('订单当前状态不可删除', 'danger') return redirect(url_for('admin_orders')) try: # 如果是待处理订单,需要恢复库存 if order.status == 'pending': for item in order.items: product = item.product product.stock += item.quantity # 删除订单项 for item in order.items: db.session.delete(item) # 删除订单 db.session.delete(order) db.session.commit() flash('订单已成功删除', 'success') except Exception as e: db.session.rollback() flash('删除订单时出错: {}'.format(str(e)), 'danger') return redirect(url_for('admin_orders')) # 添加管理员端页面管理路由 @app.route('/admin/orders')来自csdn里的哪个人 @login_required def admin_orders(): # 检查是否是管理员 if not current_user.is_authenticated or current_user.username != 'admin': flash('需要管理员权限', 'danger') return redirect(url_for('index')) # 获取所有订单,按日期降序排列 orders = Order.query.order_by(Order.date_ordered.desc()).all() return render_template('admin_orders.html', orders=orders) # 添加订单详情页面路由 @app.route('/order/<int:order_id>') @login_required def order_details(order_id): order = Order.query.get_or_404(order_id) # 检查是否是订单所有者或管理员 if order.user_id != current_user.id and current_user.username != 'admin': flash('无权限查看此订单', 'danger') return redirect(url_for('index')) return render_template('order_details.html', order=order) if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True)
05-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值