1. Flask 框架概述
Flask 是一个轻量级的 Python Web 开发框架,它基于 Werkzeug WSGI 工具包和 Jinja2 模板引擎构建。与 Django 等全栈框架不同,Flask 采用了"微框架"的设计理念,这意味着它只提供 Web 开发的核心功能,而其他功能则通过扩展实现。这种设计使得 Flask 非常灵活,开发者可以根据项目需求自由选择组件。
Flask 的核心特性包括:
- 内置开发服务器和调试器
- 集成单元测试支持
- 100% WSGI 1.0 兼容
- 基于 Unicode
- 完整的文档
- Google App Engine 兼容性
- 扩展支持
Flask 的"微"并不是指功能弱小,而是指其核心保持简单但可扩展。这种哲学使得 Flask 成为快速原型开发和小型项目的理想选择,同时也能够通过扩展支持企业级应用开发。
2. 环境准备与安装
2.1 Python 环境配置
在开始使用 Flask 之前,需要确保系统已安装 Python。推荐使用 Python 3.6 或更高版本。可以通过以下命令检查 Python 版本:
bash复制python --version
# 或
python3 --version
如果尚未安装 Python,可以从官网下载并安装最新版本。建议使用虚拟环境来管理项目依赖,避免系统 Python 环境被污染。
2.2 创建虚拟环境
虚拟环境是 Python 项目的隔离工作空间,可以单独管理每个项目的依赖。创建虚拟环境的步骤如下:
bash复制# 创建项目目录
mkdir flask_project
cd flask_project
# 创建虚拟环境
python -m venv venv
激活虚拟环境:
- Windows:
bash复制
venv\Scripts\activate - macOS/Linux:
bash复制source venv/bin/activate
激活后,命令行提示符前会显示虚拟环境名称,表示已进入隔离环境。
2.3 安装 Flask
在激活的虚拟环境中,使用 pip 安装 Flask:
bash复制pip install flask
验证安装是否成功:
bash复制python -c "import flask; print(flask.__version__)"
3. 第一个 Flask 应用
3.1 最小应用示例
创建一个名为 app.py 的文件,写入以下内容:
python复制from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
这个简单的应用做了以下几件事:
- 导入 Flask 类
- 创建 Flask 应用实例
- 使用
@app.route装饰器定义路由 - 定义视图函数返回响应
- 启动开发服务器
3.2 运行应用
在命令行执行:
bash复制python app.py
默认情况下,Flask 会启动一个开发服务器,监听 http://127.0.0.1:5000/。打开浏览器访问该地址,将看到 "Hello, World!" 的响应。
3.3 开发服务器选项
app.run() 方法支持多个参数:
host: 指定主机,默认为 127.0.0.1port: 指定端口,默认为 5000debug: 是否启用调试模式
例如,要允许局域网访问并启用调试模式:
python复制if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
调试模式会提供以下功能:
- 自动重载代码更改
- 详细的错误页面
- 交互式调试器
4. Flask 核心概念
4.1 路由系统
路由是将 URL 映射到视图函数的机制。Flask 使用 @app.route 装饰器定义路由:
python复制@app.route('/user/<username>')
def show_user_profile(username):
return f'User {username}'
路由可以包含变量部分,用 <variable_name> 表示,这些变量会作为参数传递给视图函数。
Flask 支持以下变量类型:
string: (默认) 接受任何不包含斜杠的文本int: 接受正整数float: 接受正浮点数path: 类似 string 但包含斜杠uuid: 接受 UUID 字符串
示例:
python复制@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post {post_id}'
4.2 请求对象
Flask 通过全局 request 对象访问请求数据。需要先从 flask 模块导入:
python复制from flask import request
常用属性和方法:
request.method: HTTP 方法 (GET, POST 等)request.args: GET 参数request.form: POST 表单数据request.files: 上传的文件request.headers: 请求头request.json: JSON 数据
示例:处理表单提交
python复制@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# 验证逻辑...
return f'Logged in as {username}'
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=password name=password>
<p><input type=submit value=Login>
</form>
'''
4.3 响应处理
视图函数可以返回多种类型的响应:
- 字符串: 自动转换为 Response 对象
- 元组: (response, status_code) 或 (response, headers)
- Response 对象: 完全控制响应
创建自定义响应:
python复制from flask import make_response
@app.route('/custom')
def custom_response():
response = make_response('Custom Response')
response.headers['X-Custom-Header'] = 'Value'
response.status_code = 201
return response
重定向和错误处理:
python复制from flask import redirect, url_for, abort
@app.route('/redirect')
def redir():
return redirect(url_for('hello_world'))
@app.route('/error')
def force_error():
abort(404) # 返回 404 错误
4.4 模板渲染
Flask 使用 Jinja2 模板引擎。模板文件通常存放在项目目录下的 templates 文件夹中。
示例模板 templates/hello.html:
html复制<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
渲染模板:
python复制from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
Jinja2 模板支持控制结构、继承、宏等高级功能,可以极大简化 HTML 生成。
5. Flask 项目结构
随着项目规模增长,合理的项目结构变得重要。一个典型的 Flask 项目可能如下组织:
code复制/flask_project
/venv # 虚拟环境
/app
/templates # 模板文件
/static # 静态文件 (CSS, JS, 图片)
/views # 视图模块
__init__.py
auth.py
blog.py
__init__.py # 应用工厂
models.py # 数据模型
extensions.py # Flask 扩展
config.py # 配置文件
requirements.txt # 依赖列表
run.py # 启动脚本
5.1 应用工厂模式
Flask 推荐使用应用工厂模式创建应用实例,便于测试和配置管理。修改 app/__init__.py:
python复制from flask import Flask
from . import views
def create_app(config_filename=None):
app = Flask(__name__)
if config_filename:
app.config.from_pyfile(config_filename)
views.init_app(app)
return app
5.2 蓝图 (Blueprints)
蓝图用于组织大型应用的路由和视图。创建 app/views/auth.py:
python复制from flask import Blueprint
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login')
def login():
return 'Login Page'
@auth_bp.route('/logout')
def logout():
return 'Logout Page'
在 app/__init__.py 中注册蓝图:
python复制def create_app(config_filename=None):
app = Flask(__name__)
# ...
from .views import auth
app.register_blueprint(auth.auth_bp, url_prefix='/auth')
return app
6. 数据库集成
6.1 Flask-SQLAlchemy
Flask-SQLAlchemy 是 Flask 的 SQLAlchemy 扩展,简化了数据库操作。安装:
bash复制pip install flask-sqlalchemy
配置数据库 URI:
python复制# config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
初始化扩展:
python复制# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
在应用工厂中初始化:
python复制# app/__init__.py
from .extensions import db
def create_app(config_filename=None):
app = Flask(__name__)
app.config.from_object('config.Config')
db.init_app(app)
# ...
6.2 定义模型
创建数据模型 app/models.py:
python复制from .extensions import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
def __repr__(self):
return f'<User {self.username}>'
6.3 数据库迁移
使用 Flask-Migrate 处理数据库迁移:
bash复制pip install flask-migrate
初始化:
python复制# app/extensions.py
from flask_migrate import Migrate
migrate = Migrate()
python复制# app/__init__.py
from .extensions import migrate
def create_app(config_filename=None):
# ...
migrate.init_app(app, db)
# ...
创建迁移仓库并执行初始迁移:
bash复制flask db init
flask db migrate -m "initial migration"
flask db upgrade
7. 用户认证
7.1 密码哈希
使用 Werkzeug 的安全工具进行密码哈希:
python复制from werkzeug.security import generate_password_hash, check_password_hash
class User(db.Model):
# ...
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)
7.2 登录管理
Flask-Login 处理用户会话:
bash复制pip install flask-login
配置:
python复制# app/extensions.py
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
python复制# app/models.py
from .extensions import login_manager
@login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
保护路由:
python复制from flask_login import login_required, current_user
@app.route('/profile')
@login_required
def profile():
return f'Profile page for {current_user.username}'
8. 部署上线
8.1 生产服务器
开发服务器不适合生产环境。常用的生产 WSGI 服务器包括:
- Gunicorn
- uWSGI
- Waitress
安装 Gunicorn:
bash复制pip install gunicorn
运行应用:
bash复制gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()"
8.2 静态文件
生产环境应使用 Web 服务器(如 Nginx)处理静态文件。Nginx 配置示例:
nginx复制server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static {
alias /path/to/app/static;
expires 30d;
}
}
8.3 环境变量
敏感配置应通过环境变量设置:
bash复制export SECRET_KEY='your-secret-key'
export DATABASE_URL='postgresql://user:password@localhost/dbname'
在 Flask 中读取:
python复制app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
9. 常见扩展推荐
Flask 的强大之处在于其丰富的扩展生态系统:
- Flask-WTF: 表单处理
- Flask-Mail: 电子邮件支持
- Flask-RESTful: 构建 REST API
- Flask-Caching: 缓存支持
- Flask-Admin: 管理界面
- Flask-SocketIO: WebSocket 支持
- Flask-Babel: 国际化支持
- Flask-Testing: 测试工具
安装扩展通常只需要简单的 pip 安装和在应用中的初始化:
bash复制pip install flask-wtf
python复制from flask_wtf import CSRFProtect
csrf = CSRFProtect()
def create_app():
app = Flask(__name__)
csrf.init_app(app)
# ...
10. 测试与调试
10.1 单元测试
Flask 提供了测试客户端,可以模拟请求:
python复制import unittest
from app import create_app
class BasicTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.app.config['TESTING'] = True
self.client = self.app.test_client()
def test_home_page(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
self.assertIn(b'Hello, World!', response.data)
运行测试:
bash复制python -m unittest discover
10.2 调试技巧
-
使用 Flask 的调试模式:
python复制app.run(debug=True) -
使用 pdb 进行交互式调试:
python复制import pdb; pdb.set_trace() -
记录日志:
python复制app.logger.debug('Debug message') app.logger.warning('Warning message') app.logger.error('Error message') -
使用 Flask-DebugToolbar:
bash复制
pip install flask-debugtoolbarpython复制from flask_debugtoolbar import DebugToolbarExtension toolbar = DebugToolbarExtension() def create_app(): app = Flask(__name__) app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False toolbar.init_app(app) # ...
11. 性能优化
11.1 数据库优化
-
使用索引加速查询:
python复制class User(db.Model): username = db.Column(db.String(64), index=True) -
批量操作代替循环:
python复制# 不好 for item in items: db.session.add(item) # 好 db.session.add_all(items) -
使用 selectinload 避免 N+1 查询:
python复制from sqlalchemy.orm import selectinload users = User.query.options(selectinload(User.posts)).all()
11.2 缓存策略
使用 Flask-Caching:
bash复制pip install flask-caching
配置:
python复制from flask_caching import Cache
cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})
def create_app():
app = Flask(__name__)
cache.init_app(app)
# ...
缓存视图:
python复制@app.route('/expensive-view')
@cache.cached(timeout=50)
def expensive_view():
# 耗时计算
return render_template('expensive.html')
11.3 异步任务
对于耗时任务,使用 Celery 或 RQ 异步处理:
bash复制pip install celery
配置:
python复制from celery import Celery
def make_celery(app):
celery = Celery(
app.import_name,
backend=app.config['CELERY_RESULT_BACKEND'],
broker=app.config['CELERY_BROKER_URL']
)
celery.conf.update(app.config)
return celery
celery = make_celery(app)
@celery.task
def send_async_email(email_data):
# 发送邮件逻辑
pass
12. 安全最佳实践
12.1 跨站请求伪造 (CSRF) 防护
Flask-WTF 提供 CSRF 保护:
python复制from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
csrf.init_app(app)
# ...
在表单中添加 CSRF 令牌:
html复制<form method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<!-- 其他表单字段 -->
</form>
12.2 跨站脚本 (XSS) 防护
Jinja2 自动转义模板变量,但需要注意:
- 使用
|safe过滤器时要谨慎 - 避免直接渲染用户输入
12.3 SQL 注入防护
SQLAlchemy 自动处理参数化查询,避免直接拼接 SQL 字符串:
python复制# 不安全
User.query.filter(f"username = '{username}'")
# 安全
User.query.filter_by(username=username)
# 或
User.query.filter(User.username == username)
12.4 安全头部
使用 Flask-Talisman 添加安全头部:
bash复制pip install flask-talisman
python复制from flask_talisman import Talisman
talisman = Talisman()
def create_app():
app = Flask(__name__)
talisman.init_app(app)
# ...
13. REST API 开发
13.1 Flask-RESTful
安装 Flask-RESTful:
bash复制pip install flask-restful
基本 API 示例:
python复制from flask_restful import Api, Resource
api = Api()
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}
def create_app():
app = Flask(__name__)
api.init_app(app)
api.add_resource(HelloWorld, '/api/hello')
# ...
13.2 请求解析
使用 reqparse 验证输入:
python复制from flask_restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name', required=True)
class Todo(Resource):
def post(self):
args = parser.parse_args()
return {'name': args['name']}
13.3 认证与授权
使用 Flask-HTTPAuth 进行 API 认证:
bash复制pip install flask-httpauth
python复制from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(username, password):
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
return user
class ProtectedResource(Resource):
@auth.login_required
def get(self):
return {'message': f'Hello, {auth.current_user().username}'}
14. 项目示例:博客系统
14.1 模型定义
python复制# app/models.py
from datetime import datetime
from .extensions import db
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
author = db.relationship('User', backref=db.backref('posts', lazy=True))
def __repr__(self):
return f'<Post {self.title}>'
14.2 博客蓝图
python复制# app/views/blog.py
from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_required, current_user
from .forms import PostForm
from ..models import Post, db
blog_bp = Blueprint('blog', __name__)
@blog_bp.route('/')
def index():
posts = Post.query.order_by(Post.created_at.desc()).all()
return render_template('blog/index.html', posts=posts)
@blog_bp.route('/create', methods=['GET', 'POST'])
@login_required
def create():
form = PostForm()
if form.validate_on_submit():
post = Post(
title=form.title.data,
content=form.content.data,
author=current_user
)
db.session.add(post)
db.session.commit()
flash('Your post has been created!', 'success')
return redirect(url_for('blog.index'))
return render_template('blog/create.html', form=form)
14.3 模板文件
templates/blog/index.html:
html复制{% extends "base.html" %}
{% block content %}
<h1>Blog Posts</h1>
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<p>By {{ post.author.username }} on {{ post.created_at.strftime('%Y-%m-%d') }}</p>
<p>{{ post.content }}</p>
</article>
{% endfor %}
{% endblock %}
templates/blog/create.html:
html复制{% extends "base.html" %}
{% block content %}
<h1>Create New Post</h1>
<form method="POST">
{{ form.hidden_tag() }}
<div>
{{ form.title.label }}<br>
{{ form.title(size=32) }}
</div>
<div>
{{ form.content.label }}<br>
{{ form.content(rows=10, cols=50) }}
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
{% endblock %}
15. 进阶主题
15.1 自定义 CLI 命令
Flask 基于 Click 提供命令行接口,可以添加自定义命令:
python复制import click
from flask.cli import with_appcontext
@app.cli.command('init-db')
@with_appcontext
def init_db_command():
"""Initialize the database."""
db.create_all()
click.echo('Initialized the database.')
运行:
bash复制flask init-db
15.2 信号处理
Flask 的信号系统允许在特定事件发生时执行代码:
python复制from flask import template_rendered
from contextlib import contextmanager
@contextmanager
def captured_templates(app):
recorded = []
def record(sender, template, context, **extra):
recorded.append((template, context))
template_rendered.connect(record, app)
try:
yield recorded
finally:
template_rendered.disconnect(record, app)
15.3 应用上下文
理解 Flask 的上下文机制对于开发扩展和复杂应用很重要:
- 应用上下文: 跟踪应用级别的数据
- 请求上下文: 跟踪请求级别的数据
手动推送上下文:
python复制def my_function():
with app.app_context():
# 在这里可以访问 current_app 等
user = User.query.first()
15.4 WebSocket 支持
使用 Flask-SocketIO 实现实时功能:
bash复制pip install flask-socketio
python复制from flask_socketio import SocketIO, emit
socketio = SocketIO()
def create_app():
app = Flask(__name__)
socketio.init_app(app)
# ...
@socketio.on('my_event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
emit('my_response', {'data': 'Server response'})
运行:
bash复制python -m flask run
# 或
python -m flask_socketio run
