第136集 Flask路由与视图
1. 路由与视图概述
在Flask中,路由(Routing)是指将URL与对应的处理函数(视图函数)关联起来的机制。视图函数(View Function)是处理客户端请求并返回响应的函数。
1.1 路由的作用
- 将用户请求的URL映射到相应的处理函数
- 支持动态URL参数和多种HTTP方法
- 实现应用的页面导航结构
1.2 视图函数的作用
- 处理客户端的请求
- 执行业务逻辑
- 生成响应内容
- 返回HTML、JSON、文件等各种类型的响应
2. 路由的高级用法
2.1 路由参数转换器
Flask内置了多种路由参数转换器,可以将URL中的参数转换为特定类型:
| 转换器 | 描述 | 示例 |
|---|---|---|
string |
默认类型,接受不包含斜杠的文本 | /user/<string:name> |
int |
接受正整数 | /post/<int:id> |
float |
接受正浮点数 | /price/<float:amount> |
path |
类似string,但可以包含斜杠 | /file/<path:filename> |
uuid |
接受UUID字符串 | /resource/<uuid:id> |
from flask import Flask
app = Flask(__name__)
@app.route('/user/<string:name>')
def show_user(name):
return f'User: {name}'
@app.route('/post/<int:id>')
def show_post(id):
return f'Post ID: {id}'2.2 自定义路由参数转换器
可以通过继承werkzeug.routing.BaseConverter类来创建自定义转换器:
from werkzeug.routing import BaseConverter
from flask import Flask
app = Flask(__name__)
# 自定义列表转换器,将逗号分隔的字符串转换为列表
class ListConverter(BaseConverter):
def to_python(self, value):
return value.split(',')
def to_url(self, values):
return ','.join(BaseConverter.to_url(self, item) for item in values)
# 注册自定义转换器
app.url_map.converters['list'] = ListConverter
@app.route('/items/<list:items>')
def get_items(items):
return f'Items: {items}'
# 生成URL
url = app.url_for('get_items', items=['apple', 'banana', 'cherry'])
# 输出: /items/apple,banana,cherry2.3 路由重定向
可以使用redirect()函数实现路由重定向:
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/old')
def old_route():
# 重定向到新路由
return redirect(url_for('new_route'))
@app.route('/new')
def new_route():
return 'This is the new route'
# 重定向到外部URL
@app.route('/external')
def external_route():
return redirect('https://www.example.com')2.4 路由别名
可以通过endpoint参数为路由指定别名,用于URL构建:
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/user/profile', endpoint='profile')
def show_profile():
return 'User Profile'
# 使用路由别名构建URL
url = url_for('profile') # 输出: /user/profile2.5 路由前缀
可以通过蓝图或应用配置为一组路由添加前缀:
from flask import Blueprint, Flask
app = Flask(__name__)
# 创建蓝图并指定前缀
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
# 蓝图中的路由都会自动添加/auth前缀
@auth_bp.route('/login')
def login():
return 'Login Page'
@auth_bp.route('/register')
def register():
return 'Register Page'
# 注册蓝图
app.register_blueprint(auth_bp)
# 访问/login会得到404,需要访问/auth/login2.6 路由方法
可以通过methods参数指定路由支持的HTTP方法:
from flask import Flask, request
app = Flask(__name__)
@app.route('/data', methods=['GET', 'POST', 'PUT', 'DELETE'])
def handle_data():
if request.method == 'GET':
return 'GET Request'
elif request.method == 'POST':
return 'POST Request'
elif request.method == 'PUT':
return 'PUT Request'
elif request.method == 'DELETE':
return 'DELETE Request'3. 视图函数的高级用法
3.1 视图函数的返回值类型
视图函数可以返回多种类型的响应:
3.1.1 返回字符串
@app.route('/')
def index():
return 'Hello World'3.1.2 返回元组
@app.route('/custom')
def custom_response():
# 返回(响应内容, 状态码, 响应头)
return 'Custom Response', 200, {'X-Custom-Header': 'Value'}3.1.3 返回Response对象
from flask import Flask, Response
app = Flask(__name__)
@app.route('/response')
def response_object():
response = Response('Response Object', status=200, mimetype='text/plain')
response.headers['X-Custom'] = 'Value'
return response3.1.4 返回JSON
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/json')
def json_response():
data = {'name': '张三', 'age': 25}
return jsonify(data)3.2 视图装饰器
可以使用装饰器为视图函数添加额外功能:
from flask import Flask
import functools
app = Flask(__name__)
# 定义一个装饰器,记录请求时间
def log_request_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
import time
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f'Request took {end_time - start_time:.2f} seconds')
return result
return wrapper
# 应用装饰器到视图函数
@app.route('/')
@log_request_time
def index():
return 'Hello World'3.3 视图函数中的变量作用域
注意视图函数中变量的作用域,避免使用全局变量存储请求相关的数据:
# 错误示例:使用全局变量存储用户信息
current_user = None
@app.route('/login', methods=['POST'])
def login():
global current_user
current_user = request.form['username']
return 'Login successful'
# 正确做法:使用session存储用户信息
from flask import session
app.secret_key = 'secret_key'
@app.route('/login', methods=['POST'])
def login():
session['username'] = request.form['username']
return 'Login successful'4. URL构建
Flask提供了url_for()函数来构建URL,避免硬编码URL:
4.1 基本用法
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/user/<name>')
def user_profile(name):
return f'User: {name}'
@app.route('/post/<int:id>')
def show_post(id):
return f'Post ID: {id}'
# 构建URL
with app.test_request_context():
# 构建/user/john的URL
url1 = url_for('user_profile', name='john')
# 构建/post/123的URL
url2 = url_for('show_post', id=123)
# 添加查询参数
url3 = url_for('user_profile', name='john', page=1, per_page=10)
print(url1) # /user/john
print(url2) # /post/123
print(url3) # /user/john?page=1&per_page=104.2 URL构建的好处
- 避免硬编码URL,提高代码可维护性
- 自动处理URL编码
- 支持动态参数和查询参数
- 与路由配置保持一致
4.3 构建静态文件URL
# 构建静态文件URL
url = url_for('static', filename='css/style.css')
# 输出: /static/css/style.css5. 请求钩子
请求钩子(Request Hooks)是在请求处理过程中执行的函数,用于在请求前后执行特定操作:
5.1 常用的请求钩子
5.1.1 before_first_request
在处理第一个请求之前执行:
@app.before_first_request
def before_first_request():
print('This runs before the first request')5.1.2 before_request
在每次请求之前执行:
@app.before_request
def before_request():
print('This runs before every request')5.1.3 after_request
在每次请求之后执行(如果没有异常):
@app.after_request
def after_request(response):
print('This runs after every successful request')
return response # 必须返回response对象5.1.4 teardown_request
在每次请求之后执行(无论是否有异常):
@app.teardown_request
def teardown_request(exception):
print('This runs after every request, even if an exception occurs')5.1.5 teardown_appcontext
在应用上下文被销毁时执行:
@app.teardown_appcontext
def teardown_appcontext(exception):
print('This runs when the application context is destroyed')5.2 请求钩子的应用场景
- 初始化数据库连接
- 记录请求日志
- 验证用户身份
- 设置响应头
- 清理资源
6. 基于类的视图
Flask支持基于类的视图(Class-Based Views, CBV),与基于函数的视图(Function-Based Views, FBV)相比,具有更好的代码组织和复用性。
6.1 基本用法
from flask import Flask, render_template
from flask.views import View
app = Flask(__name__)
# 继承View类创建基于类的视图
class HelloView(View):
def dispatch_request(self):
return 'Hello from Class-Based View'
# 注册视图
app.add_url_rule('/', view_func=HelloView.as_view('hello'))6.2 内置的基于类的视图
Flask提供了一些常用的基于类的视图:
6.2.1 MethodView
根据HTTP方法路由到不同的处理函数:
from flask.views import MethodView
class DataView(MethodView):
def get(self):
return 'GET Request'
def post(self):
return 'POST Request'
def put(self):
return 'PUT Request'
def delete(self):
return 'DELETE Request'
# 注册视图,自动处理不同的HTTP方法
app.add_url_rule('/data', view_func=DataView.as_view('data'))6.2.2 TemplateView
渲染模板的视图:
from flask.views import TemplateView
class IndexView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['message'] = 'Hello from TemplateView'
return context
app.add_url_rule('/', view_func=IndexView.as_view('index'))6.2.3 RedirectView
重定向视图:
from flask.views import RedirectView
class RedirectToHomeView(RedirectView):
url = '/'
class RedirectToProfileView(RedirectView):
# 使用endpoint而不是url
endpoint = 'profile'
permanent = True # 301永久重定向
app.add_url_rule('/go-home', view_func=RedirectToHomeView.as_view('go_home'))
app.add_url_rule('/go-profile', view_func=RedirectToProfileView.as_view('go_profile'))6.3 基于类的视图的优势
- 代码复用性更好
- 支持多重继承
- HTTP方法分离更清晰
- 便于扩展和维护
7. 蓝图(Blueprint)
蓝图是Flask中用于组织路由和视图的方式,可以将应用分解为多个模块:
7.1 蓝图的创建
from flask import Blueprint
# 创建蓝图
main_bp = Blueprint('main', __name__)
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')7.2 在蓝图中定义路由
# 在main_bp蓝图中定义路由
@main_bp.route('/')
def index():
return 'Home Page'
@main_bp.route('/about')
def about():
return 'About Page'
# 在auth_bp蓝图中定义路由(自动添加/auth前缀)
@auth_bp.route('/login')
def login():
return 'Login Page'
@auth_bp.route('/register')
def register():
return 'Register Page'7.3 注册蓝图
from flask import Flask
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(main_bp)
app.register_blueprint(auth_bp)7.4 蓝图的模板和静态文件
7.4.1 蓝图的模板目录
# 创建蓝图时指定模板目录
bp = Blueprint('admin', __name__, template_folder='templates')7.4.2 在蓝图中使用模板
@bp.route('/admin')
def admin():
return render_template('admin/index.html')7.4.3 蓝图的静态文件
# 创建蓝图时指定静态目录
bp = Blueprint('admin', __name__, static_folder='static')7.4.4 在模板中引用蓝图的静态文件
<link rel="stylesheet" href="{{ url_for('admin.static', filename='css/admin.css') }}">7.5 蓝图的请求钩子
# 为蓝图注册请求钩子
@bp.before_request
def bp_before_request():
print('This runs before every request in the blueprint')7.6 蓝图的错误处理
# 为蓝图注册错误处理
@bp.errorhandler(404)
def bp_page_not_found(error):
return render_template('admin/404.html'), 4047.7 蓝图的优势
- 模块化组织代码
- 支持模板和静态文件的隔离
- 便于团队协作开发
- 提高代码的可维护性
8. 路由与视图的最佳实践
8.1 路由设计原则
- 使用有意义的URL路径
- 保持URL的简洁性
- 遵循RESTful设计原则
- 避免过深的URL层级
- 使用HTTP方法表达操作意图
8.2 视图函数设计原则
- 保持视图函数的简洁性
- 避免在视图函数中编写复杂的业务逻辑
- 使用装饰器复用代码
- 适当使用基于类的视图
8.3 蓝图使用原则
- 按照功能模块划分蓝图
- 为蓝图设置合理的URL前缀
- 使用蓝图隔离模板和静态文件
- 避免蓝图之间的循环依赖
8.4 URL构建原则
- 始终使用
url_for()构建URL - 避免硬编码URL
- 使用路由别名提高代码可读性
8.5 请求钩子使用原则
- 不要在请求钩子中执行耗时操作
- 避免在请求钩子中修改请求数据
- 合理使用不同类型的请求钩子
9. 综合示例
下面是一个综合示例,展示了路由与视图的各种高级用法:
from flask import Flask, Blueprint, render_template, jsonify, redirect, url_for, request, session
from flask.views import MethodView
from werkzeug.routing import BaseConverter
# 创建应用
app = Flask(__name__)
app.secret_key = 'your-secret-key'
# 自定义URL转换器
class ListConverter(BaseConverter):
def to_python(self, value):
return value.split(',')
def to_url(self, values):
return ','.join(BaseConverter.to_url(self, item) for item in values)
app.url_map.converters['list'] = ListConverter
# 创建蓝图
api_bp = Blueprint('api', __name__, url_prefix='/api')
# 应用级别的请求钩子
@app.before_request
def before_request():
if 'username' not in session and request.endpoint not in ['auth.login', 'static']:
return redirect(url_for('auth.login'))
# 认证蓝图
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('main.index'))
return render_template('login.html')
@auth_bp.route('/logout')
def logout():
session.pop('username', None)
return redirect(url_for('auth.login'))
# 主蓝图
main_bp = Blueprint('main', __name__)
@main_bp.route('/')
def index():
return render_template('index.html', username=session.get('username'))
@main_bp.route('/user/<string:name>')
def user_profile(name):
return render_template('profile.html', name=name)
# API蓝图中的基于类的视图
class DataView(MethodView):
def get(self):
return jsonify({'message': 'GET request'})
def post(self):
return jsonify({'message': 'POST request'})
api_bp.add_url_rule('/data', view_func=DataView.as_view('data'))
# 自定义转换器示例
@api_bp.route('/items/<list:items>')
def get_items(items):
return jsonify({'items': items})
# 注册蓝图
app.register_blueprint(auth_bp)
app.register_blueprint(main_bp)
app.register_blueprint(api_bp)
if __name__ == '__main__':
app.run(debug=True)10. 总结
本集主要介绍了Flask路由与视图的高级用法,包括:
- 路由的高级用法:自定义URL转换器、路由重定向、路由别名、路由前缀等
- 视图函数的高级用法:多种返回值类型、视图装饰器、变量作用域
- URL构建:使用
url_for()函数构建URL的方法和优势 - 请求钩子:
before_first_request、before_request、after_request等钩子的使用 - 基于类的视图:
View、MethodView、TemplateView、RedirectView等类的使用 - 蓝图:蓝图的创建、注册、模板和静态文件处理
- 最佳实践:路由设计、视图函数设计、蓝图使用等方面的最佳实践
通过学习这些内容,你已经可以灵活运用Flask的路由与视图功能,构建结构清晰、易于维护的Web应用了。在后续的学习中,我们将深入学习Flask的模板、表单处理和数据库集成等高级功能。