Flask和@app.route(1)

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

下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的。
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

@app.route和其它装饰器

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

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

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

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

# This is our decorator
def simple_decorator(f):
    # This is the new function we're going to return
    # This function will be used in place of our original definition
    def wrapper():
        print "Entering Function"
        f()
        print "Exited Function"

    return wrapper

@simple_decorator 
def hello():
    print "Hello World"

hello()

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

Entering Function
Hello World
Exited Function

很好!

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

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

def decorator_factory(enter_message, exit_message):
    # We're going to return this decorator
    def simple_decorator(f):
        def wrapper():
            print enter_message
            f()
            print exit_message

        return wrapper

    return simple_decorator

@decorator_factory("Start", "End")
def hello():
    print "Hello World"

hello()

给我们的输出是:

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

把“app”放进“app.route”

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

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

class NotFlask():
    pass

app = NotFlask()

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

class NotFlask():
    def route(self, route_str):
        def decorator(f):
            return f

        return decorator

app = NotFlask()

@app.route("/")
def hello():
    return "Hello World!"

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

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

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

class NotFlask():
    def __init__(self):
        self.routes = {}

    def route(self, route_str):
        def decorator(f):
            self.routes[route_str] = f
            return f

        return decorator

app = NotFlask()

@app.route("/")
def hello():
    return "Hello World!"

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

class NotFlask():
    def __init__(self):
        self.routes = {}

    def route(self, route_str):
        def decorator(f):
            self.routes[route_str] = f
            return f

        return decorator

    def serve(self, path):
        view_function = self.routes.get(path)
        if view_function:
            return view_function()
        else:
            raise ValueError('Route "{}"" has not been registered'.format(path))

app = NotFlask()

@app.route("/")
def hello():
    return "Hello World!"

print app.serve("/")

我们会看到:

Hello World!

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

#-*- coding: UTF-8 -*-
import unittest


class TestNotFlask(unittest.TestCase):
    def setUp(self):
        self.app = NotFlask()

    def test_valid_route(self):
        @self.app.route('/')
        def index():
            return 'Hello World'

        self.assertEqual(self.app.serve('/'), 'Hello World')

    def test_invalid_route(self):
        with self.assertRaises(ValueError):
            self.app.serve('/invalid')

if __name__ == '__main__':  
    # unittest.main() # 用这个是最简单的,下面的用法可以同时测试多个类  
    # unittest.TextTestRunner(verbosity=2).run(suite1) # 这个等价于上述但可设置verbosity=2,省去了运行时加-v  
    suite1 = unittest.TestLoader().loadTestsFromTestCase(TestNotFlask)  
    suite2 = unittest.TestLoader().loadTestsFromTestCase(TestNotFlask)  
    suite = unittest.TestSuite([suite1, suite2])  
    unittest.TextTestRunner(verbosity=2).run(suite)

输出

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/

  • 9
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您的代码已经是一个良好的起点,但有一些可以进行优化的地方。下面是对您提供的代码进行优化的建议: 1. 使用密码哈希:在数据库中存储密码的哈希值而不是明文密码,可以提高安全性。 2. 使用表单验证:在服务器端进行用户名和密码的验证,而不是直接执行SQL查询。 3. 使用`flash`消息:使用`flash`消息来向用户显示错误信息,而不是将错误信息作为参数传递给模板。 4. 重命名路由:将路由函数的名称更改为更具描述性和一致性的名称。 下面是优化后的代码示例: ```python from flask import Flask, render_template, request, redirect, url_for, session, flash from werkzeug.security import check_password_hash app = Flask(__name__) app.secret_key = 'your_secret_key' @app.route('/') def home(): if 'username' not in session: return redirect(url_for('login')) else: return redirect(url_for('index')) @app.route('/index') def index(): return render_template('index.html') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') # 查询数据库检查用户名和密码是否匹配 query = "SELECT * FROM users WHERE username=%s" values = (username,) cursor.execute(query, values) user = cursor.fetchone() if user and check_password_hash(user['password'], password): session['username'] = user['username'] flash('登录成功', 'success') return redirect(url_for('index')) else: flash('用户名或密码错误', 'error') return redirect(url_for('login')) else: return render_template('login.html') if __name__ == '__main__': app.run() ``` 注意,上述代码假设您已经在数据库中存储了用户的密码哈希值,并且已经配置了数据库连接。请根据您的实际情况进行相应的修改。此外,还需要根据您的实际需求来调整`index.html`和`login.html`模板文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值