🕐 预计用时:2-3 小时 | 🎯 目标:掌握 Flask 基础,搭建你的第一个 Web 应用
Flask 是一个轻量级的 Python Web 框架。 它只提供最核心的功能——路由和请求处理,其余的(数据库、用户认证、表单验证)都由你自己选择插件来补充。
想象你要开一家餐厅:
🏪 Django = 全包式装修套餐
厨房、桌椅、菜单、收银系统全给你配好,开箱即用,但改动麻烦。
🍕 Flask = 毛坯房 + 自己装修
给你一间空房(路由 + 请求处理),你想怎么装修就怎么装。
想要数据库?自己选。想要用户系统?自己搭。自由度极高。| Flask | ||
| Flask | ||
| Flask | ||
💡 Flask vs Django 的哲学差异:
Flask 遵循"微框架"哲学——只做好两件事:路由(哪个 URL 对应哪个函数)和模板(如何把数据渲染成 HTML)。其余一切,由你按需添加。
Django 则是"全栈框架"——自带 ORM、Admin 后台、用户认证、表单处理……"Batteries included"。
# 推荐先创建虚拟环境(每个项目独立的依赖空间)
python -m venv venv
# 激活虚拟环境
# Windows:
venv\Scripts\activate
# Mac/Linux:
source venv/bin/activate
# 安装 Flask
pip install flaskpython -c "import flask; print(flask.__version__)"
# 输出类似: 3.1.1💡 什么是虚拟环境?
想象你有很多项目,A 项目需要 Flask 2.0,B 项目需要 Flask 3.0。如果都装在系统 Python 里,版本冲突就炸了。
虚拟环境 = 每个项目一个独立的房间,互不干扰。就像你给每个孩子一个独立的玩具箱,不会抢玩具。
创建一个文件 app.py,只需要 5 行代码:
# app.py
from flask import Flask
app = Flask(__name__) # 创建 Flask 应用实例
@app.route('/') # 定义路由:访问首页时
def hello():
return 'Hello, Flask!' # 返回这个字符串
if __name__ == '__main__':
app.run(debug=True) # 启动开发服务器运行它:
python app.py
# 输出:
# * Running on http://127.0.0.1:5000
# * Debug mode: on打开浏览器访问 http://127.0.0.1:5000,你会看到页面上显示 Hello, Flask!
🎉 恭喜!你刚刚搭建了人生第一个 Web 应用!
虽然只有一行字,但背后的流程是完整的:浏览器发送请求 → Flask 路由匹配 → 视图函数处理 → 返回响应 → 浏览器渲染。
Flask(__name__) | ||
@app.route('/') | ||
def hello() | ||
return '...' | ||
app.run(debug=True) |
路由就是 URL 和函数之间的映射关系。
# 当用户访问 /about 时,执行 about() 函数
@app.route('/about')
def about():
return '关于我们页面'
# 当用户访问 /contact 时,执行 contact() 函数
@app.route('/contact')
def contact():
return '联系我们页面'有时候 URL 里需要携带数据,比如 /user/123 表示查看 ID 为 123 的用户:
# 尖括号 <name> 是动态参数
@app.route('/user/<username>')
def show_user(username):
return f'用户主页:{username}'
# 访问 /user/alice → "用户主页:alice"
# 访问 /user/bob → "用户主页:bob"<name> | /user/alice | ||
<int:id> | /post/42 | ||
<float:price> | /item/9.99 | ||
<path:p> | /files/a/b/c |
@app.route('/post/<int:post_id>')
def show_post(post_id):
# post_id 已经是 int 类型,可以直接做数学运算
return f'文章 #{post_id},上一篇是 #{post_id - 1}'
# 访问 /post/42 → "文章 #42,上一篇是 #41"
# 访问 /post/abc → 404 Not Found(abc 不是整数)💡 路由匹配就像快递分拣:@app.route('/') → "所有寄到总部的包裹,交给我处理!"@app.route('/user/<name>') → "所有寄到 /user/xxx 的包裹,我来!名字记在包裹上。"@app.route('/post/<int:id>') → "所有寄到 /post/数字 的包裹交给我,但必须是数字!字母的拒收(404)。"
默认情况下,路由只响应 GET 请求。如果你需要处理表单提交(POST),需要显式声明:
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return '处理登录表单'
else:
return '显示登录页面'视图函数就是处理请求并返回响应的函数。 它是"接待员"——接收访客(请求),准备礼物(响应),然后送出。
from flask import Flask, jsonify, make_response
app = Flask(__name__)
# 1. 返回纯文本
@app.route('/text')
def text_view():
return '这是纯文本'
# 2. 返回 HTML(可以包含标签)
@app.route('/html')
def html_view():
return '<h1>这是 HTML 标题</h1><p>这是段落</p>'
# 3. 返回 JSON(API 接口常用)
@app.route('/api/data')
def api_data():
return jsonify({
'name': 'Alice',
'age': 25,
'hobbies': ['Python', 'Flask', 'Web']
})
# 4. 自定义响应头
@app.route('/custom')
def custom_response():
resp = make_response('自定义响应')
resp.headers['X-Custom'] = 'MyValue'
resp.status_code = 200
return resp💡 返回值的奥秘:
Flask 视图函数的 return 可以是:
• 字符串 → 自动转为 200 响应
• 元组(body, status_code) → 自定义状态码
• Response 对象 → 完全控制响应
直接在 Python 里拼接 HTML 字符串?可以,但噩梦般的体验:
# ❌ 反面教材:在 Python 里拼 HTML
@app.route('/user/<name>')
def bad_example(name):
return f'''
<html>
<body>
<h1>Hello, {name}</h1>
<p>欢迎来到我的网站</p>
</body>
</html>
'''问题:HTML 和 Python 混在一起,改样式要改 Python 代码,维护噩梦!
模板引擎把 Python 逻辑和 HTML 展示分开:
# ✅ 正确做法:使用模板
from flask import render_template
@app.route('/user/<name>')
def show_user(name):
return render_template('user.html', username=name)模板文件 templates/user.html:
<!DOCTYPE html>
<html>
<body>
<h1>Hello, {{ username }}</h1>
<p>欢迎来到我的网站</p>
</body>
</html>{{ variable }} | {{ name }} | |
{{ expr | filter }} | {{ name | upper }} | |
{% for %} | {% for item in list %} | |
{% if %} | {% if user %} | |
{# comment #} | {# 这是注释 #} | |
{% raw %} |
# Python 代码
@app.route('/fruits')
def fruits():
fruit_list = ['🍎 苹果', '🍌 香蕉', '🍇 葡萄', '🍊 橘子']
return render_template('fruits.html', fruits=fruit_list)<!-- templates/fruits.html -->
<h2>水果清单</h2>
<ul>
{% for fruit in fruits %}
<li>{{ fruit }}</li>
{% endfor %}
</ul><!-- templates/welcome.html -->
{% if user %}
<h2>欢迎回来,{{ user }}!</h2>
{% else %}
<h2>你好,请登录</h2>
{% endif %}upper | {{ "hello" | upper }} | |
lower | {{ "HELLO" | lower }} | |
title | {{ "hello world" | title }} | |
length | {{ [1,2,3] | length }} | |
default | {{ x | default('N/A') }} | |
join | {{ ['a','b'] | join(',') }} | |
truncate | {{ long_text | truncate(50) }} |
来做一个完整的例子——一个简单的个人主页:
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
user = {
'name': '小明',
'age': 25,
'skills': ['Python', 'Flask', 'HTML', 'CSS'],
'is_admin': True
}
posts = [
{'title': 'Flask 入门', 'date': '2026-05-12'},
{'title': 'Python 学习笔记', 'date': '2026-05-10'},
{'title': 'Web 开发心得', 'date': '2026-05-08'},
]
return render_template('home.html', user=user, posts=posts)
if __name__ == '__main__':
app.run(debug=True)<!-- templates/home.html -->
<!DOCTYPE html>
<html>
<head><title>{{ user.name }}的主页</title></head>
<body>
<h1>👋 你好,我是 {{ user.name }}</h1>
<p>年龄:{{ user.age }}</p>
<h2>技能列表</h2>
<ul>
{% for skill in user.skills %}
<li>{{ skill }}</li>
{% endfor %}
</ul>
{% if user.is_admin %}
<p style="color: red;">🔑 管理员权限</p>
{% endif %}
<h2>最新文章</h2>
<table border="1" cellpadding="8">
<tr><th>标题</th><th>日期</th></tr>
{% for post in posts %}
<tr>
<td>{{ post.title }}</td>
<td>{{ post.date }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>💡 模板继承预告:
这里每个模板都写了完整的 <html><head><body>,如果有 100 个页面,改个导航栏就要改 100 个文件?别担心,Day52 会教你模板继承,一个 base.html 搞定所有页面的公共部分。
开发时,你希望代码改了之后自动刷新,出错时看到详细的错误页面。Debug 模式就是为此而生的。
# 方式1:代码中开启
app.run(debug=True)
# 方式2:设置环境变量(推荐生产环境)
# 终端执行:
export FLASK_DEBUG=1
flask run| 自动重载 | ||
| 交互式调试器 |
⚠️ 重要安全警告:
永远不要在生产环境开启 debug=True!
Debug 调试器允许在服务器上执行任意 Python 代码。如果暴露在公网上,等于把服务器的钥匙交给了黑客。
生产环境应该用 app.run(debug=False),或者用 Gunicorn/uWSGI 作为 WSGI 服务器。
故意写一个有 bug 的代码,看看调试器长什么样:
@app.route('/divide')
def divide():
a = 10
b = 0
return str(a / b) # ZeroDivisionError!访问 /divide,浏览器会显示:
创建一个有以下页面的 Flask 应用:
# 要求:
# / → 首页,显示"欢迎来到我的网站"
# /about → 关于页面,显示个人简介
# /projects → 项目页面,展示 3 个项目
# /contact → 联系页面,显示联系方式# 要求:
# /greet/<name> → 显示"你好,{name}!"
# /greet/<name>/<lang> → 根据 lang 显示不同语言的问候
# - lang='cn' → "你好,{name}!"
# - lang='en' → "Hello, {name}!"
# - lang='jp' → "こんにちは、{name}!"
# - 其他 → "Hi, {name}!"# 要求:
# /calc/<float:a>/<op>/<float:b>
# - op='add' → 返回 a + b 的结果
# - op='sub' → 返回 a - b 的结果
# - op='mul' → 返回 a * b 的结果
# - op='div' → 返回 a / b 的结果(注意除零!)
# - 其他 op → 返回 "不支持的运算"
#
# 提示:用 jsonify 返回 JSON 格式的结果🚀 明日预告:Day 52 — 模板进阶
今天你已经能用模板渲染页面了,但每个模板都写了完整的 HTML 结构。明天我们会学模板继承——一个 base.html 管理所有页面的公共部分,还有宏、自定义过滤器和静态文件管理。告别重复代码,让模板更优雅!