Python Flask 框架入门
目录
学习目标:掌握 Flask 微框架的核心概念,理解其灵活、轻量的设计理念,能够构建包含路由、模板、数据库的 Web 应用。
1. Flask 简介
Flask 是一个轻量级的 Python Web 框架,被称为 “微框架”,因为它核心精简但可通过扩展灵活增强。
1.1 设计哲学
- 核心精简:只提供路由、请求处理、模板等基础功能
- 灵活扩展:通过扩展包添加数据库、认证等功能
- 自由组合:开发者可选择最适合的组件
1.2 Flask vs Django
| 特性 | Flask | Django |
|---|---|---|
| 定位 | 微框架 | 全栈框架 |
| 组件 | 按需添加 | 内置电池 |
| 学习曲线 | 平缓 | 较陡 |
| 数据库 | 自由选择(SQLAlchemy 等) | 内置 ORM |
| 模板 | Jinja2(内置) | Django 模板 |
| 管理后台 | 需扩展 | 内置 |
| 适用场景 | API、微服务、小型项目 | 大型应用、快速开发 |
2. 快速开始
2.1 安装与基础应用
# 安装 Flask
pip install flask
# 可选:安装常用扩展
pip install flask-sqlalchemy flask-wtf flask-login# app.py - 最小的 Flask 应用
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello Flask!'
if __name__ == '__main__':
app.run(debug=True, port=5000)2.2 运行应用
# 方式1:直接运行
python app.py
# 方式2:使用环境变量
export FLASK_APP=app.py
export FLASK_ENV=development # 开发模式(自动重载、调试信息)
flask run --host=0.0.0.0 --port=5000
# 方式3:生产环境(使用 Gunicorn)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 app:app3. 路由系统
3.1 基本路由
from flask import Flask
app = Flask(__name__)
# 静态路由
@app.route('/')
def index():
return '首页'
@app.route('/about')
def about():
return '关于我们'
# 动态路由 - 变量规则
@app.route('/user/<username>')
def show_user(username):
return f'用户: {username}'
# 指定类型
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'文章 ID: {post_id}, 类型: {type(post_id).__name__}'
@app.route('/price/<float:amount>')
def show_price(amount):
return f'价格: ¥{amount:.2f}'
@app.route('/path/<path:subpath>')
def show_path(subpath):
return f'路径: {subpath}'
# HTTP 方法
@app.route('/api/data', methods=['GET', 'POST'])
def handle_data():
from flask import request
if request.method == 'POST':
return '创建数据'
return '获取数据'3.2 URL 构建
from flask import url_for, redirect
@app.route('/login')
def login():
return '登录页'
@app.route('/profile/<username>')
def profile(username):
return f'{username} 的个人中心'
with app.test_request_context():
# 生成 URL
print(url_for('login')) # /login
print(url_for('profile', username='john')) # /profile/john
print(url_for('profile', username='john', next='/dashboard'))
# /profile/john?next=%2Fdashboard4. 请求与响应
4.1 请求对象
from flask import Flask, request
app = Flask(__name__)
@app.route('/request-info', methods=['GET', 'POST'])
def request_info():
"""展示请求信息"""
info = {
'method': request.method,
'url': request.url,
'path': request.path,
'remote_addr': request.remote_addr,
'headers': dict(request.headers),
}
# GET 参数
if request.args:
info['query_params'] = dict(request.args)
# POST 数据
if request.form:
info['form_data'] = dict(request.form)
# JSON 数据
if request.is_json:
info['json_data'] = request.get_json()
# 文件上传
if request.files:
info['files'] = list(request.files.keys())
# Cookies
info['cookies'] = request.cookies
return info
# 文件上传示例
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return '没有文件', 400
file = request.files['file']
if file.filename == '':
return '未选择文件', 400
# 保存文件
file.save(f'uploads/{file.filename}')
return f'文件 {file.filename} 上传成功'4.2 响应构造
from flask import Flask, jsonify, make_response, redirect, abort
app = Flask(__name__)
# 字符串响应
@app.route('/text')
def text_response():
return '纯文本响应'
# JSON 响应
@app.route('/api/user')
def json_response():
return jsonify({
'id': 1,
'name': '张三',
'email': 'zhangsan@example.com'
})
# 自定义状态码和头部
@app.route('/custom')
def custom_response():
response = make_response('自定义响应', 201)
response.headers['X-Custom-Header'] = 'MyValue'
response.set_cookie('session', 'abc123')
return response
# 重定向
@app.route('/old-page')
def old_page():
return redirect('/new-page', code=301)
# 错误处理
@app.route('/user/<int:user_id>')
def get_user(user_id):
if user_id < 1:
abort(400, '无效的用户 ID')
users = {1: '张三', 2: '李四'}
if user_id not in users:
abort(404, '用户不存在')
return {'id': user_id, 'name': users[user_id]}
# 自定义错误页面
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': '页面不存在'}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({'error': '服务器内部错误'}), 5005. 模板引擎(Jinja2)
5.1 基本使用
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/hello/<name>')
def hello(name):
return render_template('hello.html', name=name, items=['A', 'B', 'C'])<!-- templates/hello.html -->
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello, {{ name|title }}!</h1>
<!-- 条件判断 -->
{% if name == 'admin' %}
<p>欢迎管理员</p>
{% else %}
<p>欢迎访客</p>
{% endif %}
<!-- 循环 -->
<ul>
{% for item in items %}
<li>{{ loop.index }}. {{ item }}</li>
{% else %}
<li>列表为空</li>
{% endfor %}
</ul>
<!-- 过滤器 -->
<p>{{ name|upper }}</p>
<p>{{ "hello world"|title }}</p>
<p>{{ 1234567|filesizeformat }}</p>
</body>
</html>5.2 模板继承
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Flask App{% endblock %}</title>
{% block head %}{% endblock %}
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('about') }}">关于</a>
</nav>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block title %}首页 - Flask App{% endblock %}
{% block content %}
<h1>欢迎来到 Flask</h1>
<p>这是一个模板继承示例</p>
{% endblock %}6. 会话与消息闪现
6.1 Session(客户端会话)
from flask import Flask, session, request, redirect, url_for
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # 用于加密 session
@app.route('/login', methods=['POST'])
def do_login():
username = request.form['username']
# 验证用户...
session['username'] = username
session['logged_in'] = True
return redirect(url_for('dashboard'))
@app.route('/dashboard')
def dashboard():
if 'username' not in session:
return redirect(url_for('login'))
return f'欢迎, {session["username"]}!'
@app.route('/logout')
def logout():
session.pop('username', None)
session.pop('logged_in', None)
return redirect(url_for('index'))6.2 消息闪现(Flash Messages)
from flask import Flask, flash, redirect, render_template
app = Flask(__name__)
app.secret_key = 'secret-key'
@app.route('/create', methods=['POST'])
def create_item():
# 创建逻辑...
flash('项目创建成功!', 'success')
return redirect(url_for('index'))
@app.route('/delete/<int:id>')
def delete_item(id):
# 删除逻辑...
flash('项目已删除', 'warning')
return redirect(url_for('index'))7. 数据库集成(SQLAlchemy)
7.1 配置与模型
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class User(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)
def __repr__(self):
return f'<User {self.username}>'
class Post(db.Model):
"""文章模型"""
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
author = db.relationship('User', backref=db.backref('posts', lazy=True))
created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
# 创建表
with app.app_context():
db.create_all()7.2 CRUD 操作
from flask import Flask, request, jsonify
# 创建
@app.route('/users', methods=['POST'])
def create_user():
data = request.get_json()
user = User(username=data['username'], email=data['email'])
db.session.add(user)
db.session.commit()
return jsonify({'id': user.id}), 201
# 查询
@app.route('/users')
def get_users():
users = User.query.all()
return jsonify([{
'id': u.id,
'username': u.username,
'email': u.email
} for u in users])
@app.route('/users/<int:user_id>')
def get_user(user_id):
user = User.query.get_or_404(user_id)
return jsonify({
'id': user.id,
'username': user.username,
'email': user.email,
'posts': [{'title': p.title} for p in user.posts]
})
# 更新
@app.route('/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
user = User.query.get_or_404(user_id)
data = request.get_json()
user.username = data.get('username', user.username)
user.email = data.get('email', user.email)
db.session.commit()
return jsonify({'message': '更新成功'})
# 删除
@app.route('/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return jsonify({'message': '删除成功'})8. 蓝图(Blueprint)
蓝图用于组织大型应用的模块化路由。
# blog.py - 博客蓝图
from flask import Blueprint, render_template
blog_bp = Blueprint('blog', __name__, url_prefix='/blog')
@blog_bp.route('/')
def index():
return render_template('blog/index.html')
@blog_bp.route('/post/<int:post_id>')
def show_post(post_id):
return f'博客文章 {post_id}'
# auth.py - 认证蓝图
from flask import Blueprint
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route('/login')
def login():
return '登录页'
@auth_bp.route('/register')
def register():
return '注册页'
# app.py - 注册蓝图
from flask import Flask
from blog import blog_bp
from auth import auth_bp
app = Flask(__name__)
app.register_blueprint(blog_bp)
app.register_blueprint(auth_bp)
# 访问:
# /blog/ → 博客首页
# /blog/post/1 → 博客文章
# /auth/login → 登录页9. 应用工厂模式
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_name='development'):
"""应用工厂"""
app = Flask(__name__)
# 加载配置
if config_name == 'development':
app.config.from_mapping(
SECRET_KEY='dev',
SQLALCHEMY_DATABASE_URI='sqlite:///dev.db',
SQLALCHEMY_TRACK_MODIFICATIONS=False
)
elif config_name == 'production':
app.config.from_pyfile('config.py')
# 初始化扩展
db.init_app(app)
# 注册蓝图
from . import blog, auth
app.register_blueprint(blog.bp)
app.register_blueprint(auth.bp)
# 注册错误处理
app.register_error_handler(404, lambda e: ('Not Found', 404))
return app
# 启动
# from app import create_app
# app = create_app('development')10. 小结
| 概念 | Flask 实现 | 说明 |
|---|---|---|
| 路由 | @app.route() |
装饰器注册 |
| 请求 | request 对象 |
全局上下文 |
| 响应 | return / jsonify() |
自动推断或显式构造 |
| 模板 | Jinja2 | 继承 Django 模板风格 |
| 会话 | session |
客户端 Cookie 存储 |
| 数据库 | Flask-SQLAlchemy | 扩展集成 |
| 模块化 | Blueprint | 应用拆分 |
11. 练习题
- 使用 Flask 创建一个 RESTful API,实现图书的增删改查(使用内存存储或 SQLite)。
- 为上述 API 添加请求参数验证和错误处理。
- 使用蓝图将应用拆分为
api和web两个模块。 - 对比 Flask 和 Django 的相同功能实现,体会两者的设计差异。
下节预告:我们将学习 FastAPI,这个现代异步框架,体验自动文档生成和类型提示的强大功能。