Python Web 开发之 Flask 框架入门
一、Flask 简介
Flask 是一个基于 Python 的轻量级 Web 框架,由 Armin Ronacher 开发。它的设计哲学是微框架(Micro Framework)------核心保持简洁,功能通过扩展实现。
核心特点:
- 轻量:核心代码精简,不绑定数据库、表单验证等组件
- 灵活:可自由选择 ORM、模板引擎等组件
- 易学:API 直观,上手快
- Jinja2 模板:内置强大的模板引擎
- Werkzeug WSGI:基于成熟的 WSGI 工具库
- 扩展丰富:社区提供大量高质量扩展
安装:
bash
pip install flask
二、第一个 Flask 应用
python
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
if __name__ == "__main__":
app.run(debug=True)
运行:
bash
python app.py
访问 http://127.0.0.1:5000 即可看到页面。
app.run() 常用参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
host |
监听地址 | 127.0.0.1 |
port |
监听端口 | 5000 |
debug |
开启调试模式(热重载 + 详细错误页) | False |
三、路由
3.1 基本路由
python
@app.route("/about")
def about():
return "About Page"
@app.route("/contact")
def contact():
return "Contact Page"
3.2 变量规则
python
@app.route("/user/<username>")
def show_user(username):
return f"User: {username}"
# 指定类型
@app.route("/post/<int:post_id>")
def show_post(post_id):
return f"Post ID: {post_id}"
@app.route("/price/<float:price>")
def show_price(price):
return f"Price: {price}"
# 路径类型
@app.route("/file/<path:filename>")
def show_file(filename):
return f"File: {filename}"
支持的类型转换器:
| 转换器 | 说明 |
|---|---|
string |
默认,接受不含 / 的字符串 |
int |
整数 |
float |
浮点数 |
path |
含 / 的字符串 |
uuid |
UUID 字符串 |
3.3 HTTP 方法
python
from flask import request
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
return "Login Page"
elif request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
return f"Login: {username}"
3.4 构造 URL
python
from flask import url_for
@app.route("/user/<username>")
def profile(username):
return f"Profile: {username}"
# 在视图函数或模板中使用
url_for("profile", username="alice")
# 输出: /user/alice
url_for 的优势:自动处理特殊字符和转义,修改路由规则时无需到处改 URL。
四、请求与响应
4.1 Request 对象
python
from flask import request
@app.route("/search")
def search():
# URL 参数: /search?q=flask&page=1
keyword = request.args.get("q")
page = request.args.get("page", 1, type=int)
# 请求头
user_agent = request.headers.get("User-Agent")
# 客户端 IP
ip = request.remote_addr
# 请求方法
method = request.method
return f"Search: {keyword}, Page: {page}"
4.2 处理表单与 JSON
python
@app.route("/api/data", methods=["POST"])
def handle_data():
# 表单数据 (application/x-www-form-urlencoded / multipart)
name = request.form.get("name")
# JSON 数据 (application/json)
json_data = request.get_json()
title = json_data.get("title")
# 原始请求体
raw_data = request.data
return {"status": "ok", "name": name}
4.3 Response 对象
python
from flask import jsonify, make_response
@app.route("/api/info")
def info():
# 返回 JSON
return jsonify({"version": "1.0", "name": "myapp"})
@app.route("/download")
def download():
# 自定义响应
response = make_response("Hello World")
response.headers["Content-Type"] = "text/plain"
response.headers["X-Custom"] = "value"
response.set_cookie("session_id", "abc123")
return response
@app.route("/redirect-demo")
def redirect_demo():
from flask import redirect
return redirect("/about")
五、模板引擎(Jinja2)
5.1 渲染模板
项目结构:
myapp/
├── app.py
└── templates/
├── base.html
├── index.html
└── user.html
python
from flask import render_template
@app.route("/")
def index():
return render_template("index.html", title="首页", items=["Apple", "Banana"])
5.2 模板语法
html
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
html
<!-- index.html -->
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblock %}
5.3 常用模板功能
html
<!-- 条件判断 -->
{% if user %}
<p>欢迎, {{ user.name }}</p>
{% else %}
<p>请登录</p>
{% endif %}
<!-- 循环 -->
{% for post in posts %}
<h2>{{ post.title }}</h2>
{% else %}
<p>暂无文章</p>
{% endfor %}
<!-- 过滤器 -->
<p>{{ content | truncate(100) }}</p>
<p>{{ date | datetimeformat("%Y-%m-%d") }}</p>
<p>{{ name | upper }}</p>
<p>{{ content | safe }}</p> {# 不转义 HTML #}
<!-- 自定义过滤器 -->
{{ price | round(2) }}
5.4 自定义过滤器
python
@app.template_filter("reverse")
def reverse_filter(s):
return s[::-1]
# 模板中使用: {{ "hello" | reverse }} 输出 olleh
六、会话管理
6.1 Cookie
python
from flask import make_response, request
@app.route("/set-cookie")
def set_cookie():
resp = make_response("Cookie set")
resp.set_cookie("username", "alice", max_age=3600, httponly=True)
return resp
@app.route("/get-cookie")
def get_cookie():
username = request.cookies.get("username")
return f"Username: {username}"
6.2 Session
python
from flask import session
app.secret_key = "your-secret-key-here" # 生产环境务必使用安全密钥
@app.route("/login", methods=["POST"])
def login():
session["user_id"] = 42
session["username"] = "alice"
return "Login success"
@app.route("/profile")
def profile():
user_id = session.get("user_id")
if not user_id:
return "Please login", 401
return f"User ID: {user_id}"
@app.route("/logout")
def logout():
session.clear()
return "Logged out"
七、蓝图(Blueprint)------模块化开发
蓝图用于将应用拆分为多个模块,适合中大型项目。
7.1 项目结构
myapp/
├── app.py
├── auth/
│ ├── __init__.py
│ └── routes.py
├── blog/
│ ├── __init__.py
│ └── routes.py
└── templates/
7.2 定义蓝图
python
# auth/routes.py
from flask import Blueprint
auth_bp = Blueprint("auth", __name__, url_prefix="/auth")
@auth_bp.route("/login")
def login():
return "Login Page"
@auth_bp.route("/register")
def register():
return "Register Page"
python
# blog/routes.py
from flask import Blueprint
blog_bp = Blueprint("blog", __name__, url_prefix="/blog")
@blog_bp.route("/")
def index():
return "Blog Index"
@blog_bp.route("/<int:post_id>")
def show_post(post_id):
return f"Post: {post_id}"
7.3 注册蓝图
python
# app.py
from flask import Flask
from auth.routes import auth_bp
from blog.routes import blog_bp
app = Flask(__name__)
app.secret_key = "your-secret-key"
app.register_blueprint(auth_bp)
app.register_blueprint(blog_bp)
八、数据库集成(Flask-SQLAlchemy)
8.1 安装与配置
bash
pip install flask-sqlalchemy
python
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)
8.2 定义模型
python
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)
posts = db.relationship("Post", backref="author", lazy=True)
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)
8.3 增删改查
python
# 创建表
with app.app_context():
db.create_all()
# 新增
user = User(username="alice", email="alice@example.com")
db.session.add(user)
db.session.commit()
# 查询
user = User.query.filter_by(username="alice").first()
users = User.query.all()
user = User.query.get(1)
# 修改
user.email = "new@example.com"
db.session.commit()
# 删除
db.session.delete(user)
db.session.commit()
九、表单处理(Flask-WTF)
bash
pip install flask-wtf
python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
app.config["SECRET_KEY"] = "your-secret-key"
class LoginForm(FlaskForm):
username = StringField("Username", validators=[DataRequired(), Length(min=3, max=20)])
email = StringField("Email", validators=[DataRequired(), Email()])
password = PasswordField("Password", validators=[DataRequired(), Length(min=6)])
submit = SubmitField("Login")
视图函数:
python
@app.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
# 处理登录逻辑
return f"Welcome, {form.username.data}"
return render_template("login.html", form=form)
模板:
html
<form method="POST">
{{ form.hidden_tag() }}
<div>
{{ form.username.label }}
{{ form.username() }}
{% for error in form.username.errors %}
<span style="color:red">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.password.label }}
{{ form.password() }}
</div>
<div>{{ form.submit() }}</div>
</form>
十、错误处理
python
@app.errorhandler(404)
def not_found(error):
return render_template("404.html"), 404
@app.errorhandler(500)
def server_error(error):
return render_template("500.html"), 500
自定义异常:
python
class NotFoundError(Exception):
pass
@app.errorhandler(NotFoundError)
def handle_not_found(error):
return jsonify({"error": str(error)}), 404
十一、中间件与钩子
Flask 提供了请求生命周期中的钩子函数:
python
@app.before_request
def before_request():
"""每个请求前执行"""
print(f"Request: {request.path}")
@app.after_request
def after_request(response):
"""每个请求后执行,可以修改响应"""
response.headers["X-Request-ID"] = "abc123"
return response
@app.teardown_request
def teardown_request(exception):
"""请求结束后执行(无论是否异常)"""
print("Request finished")
@app.context_processor
def inject_globals():
"""向所有模板注入变量"""
return {"site_name": "MyApp"}
十二、RESTful API 开发
12.1 返回 JSON
python
@app.route("/api/users", methods=["GET"])
def get_users():
users = User.query.all()
return jsonify([{"id": u.id, "name": u.username} for u in users])
@app.route("/api/users", methods=["POST"])
def create_user():
data = request.get_json()
if not data or "username" not in data:
return jsonify({"error": "Username is required"}), 400
user = User(username=data["username"])
db.session.add(user)
db.session.commit()
return jsonify({"id": user.id, "username": user.username}), 201
@app.route("/api/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)
db.session.commit()
return jsonify({"id": user.id, "username": user.username})
@app.route("/api/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 "", 204
12.2 使用 Flask-RESTful
bash
pip install flask-restful
python
from flask_restful import Resource, Api
api = Api(app)
class UserResource(Resource):
def get(self, user_id):
user = User.query.get_or_404(user_id)
return {"id": user.id, "username": user.username}
def delete(self, user_id):
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return "", 204
api.add_resource(UserResource, "/api/users/<int:user_id>")
十三、部署
13.1 生产环境 WSGI 服务器
Flask 内置服务器不适合生产环境,推荐使用 Gunicorn:
bash
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 "app:app"
参数说明:
-w 4:4 个 worker 进程-b 0.0.0.0:8000:监听地址和端口
13.2 配置管理
python
# config.py
class Config:
DEBUG = False
SECRET_KEY = "prod-secret-key"
SQLALCHEMY_DATABASE_URI = "postgresql://user:pass@localhost/db"
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = "sqlite:///dev.db"
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = "sqlite:///test.db"
python
# app.py
app.config.from_object("config.DevelopmentConfig")
# 或从环境变量
app.config.from_envvar("APP_SETTINGS")
十四、常用扩展一览
| 扩展 | 用途 |
|---|---|
| Flask-SQLAlchemy | ORM 数据库操作 |
| Flask-Migrate | 数据库迁移(Alembic) |
| Flask-WTF | 表单验证 |
| Flask-Login | 用户认证 |
| Flask-RESTful | RESTful API |
| Flask-JWT-Extended | JWT 认证 |
| Flask-CORS | 跨域支持 |
| Flask-Caching | 缓存 |
| Flask-Mail | 邮件发送 |
| Flask-DebugToolbar | 调试工具栏 |
| Flask-Limiter | 接口限流 |
| Flask-Admin | 后台管理界面 |
十五、总结
Flask 的核心优势在于简洁与灵活:
- 小项目:单文件即可快速搭建
- 中项目:通过蓝图实现模块化
- 大项目:配合扩展可以覆盖认证、ORM、API、缓存等全套需求
学习路径建议:
路由与视图 → 模板渲染 → 请求与响应 → 表单处理
→ 数据库集成 → 蓝图模块化 → 认证授权 → RESTful API → 部署