初识Flask - Python轻量级Web框架入门指南

你有没有过这种想法? ——想用Python搭个极简的留言板,让朋友留两句话; ——写个API,把自己爬虫爬的小数据暴露出来给小程序/网页用; ——甚至弄个个人小站,放放自己的练手项目?

如果有,那Flask,这个被誉为「Python微框架天花板入门款」的东西,绝对是你首选的武器!

1. Flask简介

1.1 什么是Flask?

Flask是由Armin Ronacher在2010年创建的Python微框架,它基于成熟的Werkzeug WSGI工具箱Jinja2模板引擎,现在已成为最受欢迎的Python Web框架之一。

它的「微」不是指功能少,而是核心极致简洁——只保留了Web开发最基础的部分(路由、模板、响应),其他功能(数据库、表单验证、用户认证等)全靠扩展按需添加,完全不强制你用特定的工具或库。

1.2 Flask的核心特点

  • 轻量易上手:核心代码只有几千行,半天就能跑通第一个完整应用
  • 极高灵活性:想怎么搭项目结构、用什么数据库都随你
  • 丰富的扩展生态:官方/第三方扩展覆盖了几乎所有Web开发场景
  • 社区活跃文档全:遇到问题搜一搜就能找到解决方案

1.3 与同级别Python框架对比

特性FlaskDjangoFastAPI
学习难度极低中等中等
核心功能仅基础Web组件全栈开箱即用(ORM、Admin等)异步API开箱即用
灵活性极高较低(遵循MTV架构)中等
适用场景小型项目、练手API、原型开发中大型全栈项目高性能异步API

2. Flask安装与环境准备

2.1 环境要求

  • Python 3.7 或更高版本
  • pip包管理器
  • 虚拟环境(强烈推荐,避免污染系统Python环境)

2.2 创建虚拟环境

首先新建一个项目文件夹,比如叫flask-demo,然后在里面打开终端执行:

# 环境搭建-创建/激活虚拟环境
# 1. 创建虚拟环境(flask_env是环境名,可自定义)
python -m venv flask_env

# 2. 激活虚拟环境
# macOS/Linux
source flask_env/bin/activate
# Windows (CMD/旧PowerShell)
flask_env\Scripts\activate
如果是**新版PowerShell (Win11/Win10 20H2+)**,需要先允许本地脚本执行: ```powershell Set-ExecutionPolicy RemoteSigned -Scope CurrentUser ``` 然后用`.\flask_env\Scripts\Activate.ps1`激活。

激活成功后,终端开头会出现(flask_env)的标识,接下来所有操作都在这个隔离的环境里进行。

# 3. 升级pip(可选但推荐)
pip install --upgrade pip

2.3 安装Flask

# 安装最新稳定版Flask
pip install Flask

# 验证安装(如果输出版本号说明成功)
python -c "import flask; print(flask.__version__)"

3. 第一个Flask应用:Hello World!

敲黑板!这里的代码虽然短,但包含了Flask应用的全部核心元素

3.1 创建应用文件

flask-demo目录下新建app.py

# app.py
from flask import Flask

# 1. 创建Flask应用实例
# __name__是Flask的「导航员」——告诉它从当前文件所在目录找templates/static等资源
app = Flask(__name__)

# 2. 定义路由装饰器+处理函数
# @app.route('/')把「根路径http://127.0.0.1:5000/」映射到下面的hello_world函数
@app.route('/')
def hello_world():
    # 处理函数的返回值就是给浏览器的响应内容
    return '<h1>🎉 恭喜你,第一个Flask应用跑通了!</h1>'

# 3. 启动应用(仅在直接运行app.py时生效)
if __name__ == '__main__':
    # debug=True开启调试模式:代码修改后自动重启、显示详细错误信息
    app.run(debug=True)

3.2 运行并访问应用

在激活虚拟环境的终端里执行:

# 运行应用
python app.py

终端会显示类似这样的信息:

 * Running on http://127.0.0.1:5000/
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: xxx-xxx-xxx

打开浏览器访问http://127.0.0.1:5000/,就能看到你的Hello World页面啦!

`app.run(debug=True)`开启的调试模式**非常危险**——它会暴露代码、配置,甚至允许用户在服务器上执行Python代码! 入门练手用没关系,但正式上线一定要关掉,换成专业的WSGI服务器(比如Gunicorn)。

4. Flask核心概念一:路由系统

路由就是把URL路径和处理函数绑定起来的规则,是Flask应用的「入口管理器」。

4.1 基本路由

# 根路径
@app.route('/')
def index():
    return '首页'

# 带固定字符串的路径
@app.route('/about')
def about():
    return '关于我们'

4.2 带变量的路由(URL转换器)

你可以用<变量名>的方式在URL里加动态内容,还可以用<转换器:变量名>限制变量类型:

转换器说明示例
string默认(不含斜杠的字符串)<username>
int整数<int:user_id>
float浮点数<float:price>
path含斜杠的路径<path:file_path>
uuidUUID字符串<uuid:device_id>
# 字符串变量(默认)
@app.route('/user/<username>')
def user_profile(username):
    return f'你好,{username}!'

# 整数变量
@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'这是第 {post_id} 篇文章'

# 路径变量(含斜杠)
@app.route('/files/<path:file_path>')
def show_file(file_path):
    # 模拟显示文件路径,实际应用要注意安全!
    return f'你请求的文件路径是:{file_path}'

4.3 限制HTTP方法

默认情况下,Flask路由只接受GET请求,如果需要接受POSTPUT等方法,要在@app.route里加methods参数:

from flask import request  # 别忘了导入request对象!

# 同时接受GET和POST请求
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 处理POST请求:比如获取表单数据
        return '正在处理登录请求...'
    else:
        # 处理GET请求:比如显示登录表单
        return '<form method="post"><input type="text" name="username"><button type="submit">登录</button></form>'

# 处理GET查询参数(比如/search?q=Flask)
@app.route('/search')
def search():
    # request.args.get('参数名', '默认值')获取URL中的查询参数
    keyword = request.args.get('q', '暂无关键词')
    return f'搜索关键词:{keyword}'

5. Flask核心概念二:Jinja2模板引擎

直接在Python代码里写HTML太丑了!Flask内置了Jinja2模板引擎,让你把HTML(视图)和Python(逻辑)分开写

5.1 基本模板使用

  1. 首先在flask-demo目录下新建templates文件夹(名字必须是templates,不能改!
  2. templates里新建hello.html
<!-- templates/hello.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Jinja2变量:用{{ 变量名 }}包裹 -->
    <title>{{ page_title }}</title>
</head>
<body>
    <h1>你好,{{ user_name }}!👋</h1>

    <!-- Jinja2条件判断:用{% if ... %} {% elif ... %} {% else %} {% endif %}包裹 -->
    {% if user_age >= 18 %}
        <p>你已经成年啦!</p>
    {% else %}
        <p>你还未成年哦~</p>
    {% endif %}

    <!-- Jinja2循环:用{% for ... in ... %} {% endfor %}包裹 -->
    <h3>最近读过的书:</h3>
    <ul>
        {% for book in books %}
            <li>{{ book.title }} - {{ book.author }}</li>
        {% endfor %}
    </ul>
</body>
</html>
  1. app.py里用render_template渲染模板:
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/hello/<name>/<int:age>')
def hello_template(name, age):
    # 准备要传给模板的数据
    books = [
        {'title': 'Flask Web开发实战', 'author': '李辉'},
        {'title': 'Python编程从入门到实践', 'author': '埃里克·马瑟斯'}
    ]
    # 第一个参数是模板文件名,后面的参数是传给模板的键值对
    return render_template(
        'hello.html',
        page_title=f'{name}的主页',
        user_name=name,
        user_age=age,
        books=books
    )

5.2 模板继承(避免重复写代码)

如果你的网站有多个页面,头部、底部、导航栏都是一样的,那可以用模板继承来减少重复:

  1. 先创建一个基础模板base.html,里面放所有页面共用的内容,并用{% block 块名 %}{% endblock %}预留子页面可以修改的位置:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}小站首页{% endblock %} | Flask入门练手</title>
    <!-- 后续加静态文件CSS/JS用 -->
</head>
<body>
    <header>
        <h1>我的Flask小站</h1>
        <nav>
            <a href="/">首页</a> |
            <a href="/about">关于</a>
        </nav>
    </header>
    <!-- 主要内容块,子页面必须覆盖 -->
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>© 2024 道满PythonAI</p>
    </footer>
</body>
</html>
  1. 再创建子模板child_about.html,继承基础模板并覆盖需要修改的块:
<!-- templates/child_about.html -->
<!-- 第一行必须写继承语句 -->
{% extends "base.html" %}

<!-- 覆盖title块 -->
{% block title %}关于我们{% endblock %}

<!-- 覆盖content块 -->
{% block content %}
    <h2>关于我们</h2>
    <p>这是一个用Flask搭建的入门练手小站~</p>
{% endblock %}
  1. app.py里添加路由:
@app.route('/about')
def about():
    return render_template('child_about.html')

6. 静态文件管理(CSS/JS/图片)

CSS、JavaScript、图片这些不需要动态生成的文件,Flask会自动从static文件夹(名字必须是static,不能改!)里提供。

6.1 基本使用

  1. flask-demo目录下新建static文件夹,再在里面建cssjsimages子文件夹
  2. 比如在static/css里新建style.css
/* static/css/style.css */
body {
    background-color: #f0f2f5;
    font-family: 'Microsoft YaHei', sans-serif;
}
h1 {
    color: #1890ff;
}
  1. 在模板里用url_for('static', filename='子路径/文件名')生成静态文件的URL:
<!-- 修改templates/base.html的head部分 -->
<head>
    ...
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>

7. 完整练手项目:简易留言板

现在我们把前面学的内容整合起来,做一个可以留言、删除留言的小应用

7.1 项目结构

flask-demo/
├── app.py              # 应用主文件
├── flask_env/          # 虚拟环境(不用管)
├── static/             # 静态文件
│   └── css/
│       └── style.css
└── templates/          # 模板文件
    ├── base.html
    └── index.html

7.2 代码实现

1. 先修改base.htmlstyle.css(可以参考第5、6节的内容,稍微优化一下视觉效果,更像留言板)

2. 然后修改app.py

from flask import Flask, render_template, request, redirect, url_for, flash
from datetime import datetime

app = Flask(__name__)
# 必须设置SECRET_KEY才能使用flash消息!
# 实际应用中要用随机生成的密钥,不要用硬编码的
app.secret_key = 'your-random-secret-key-here'

# 模拟数据存储(内存里的列表,重启应用数据会清空,学了数据库后可以换成MySQL/SQLite)
messages = []

# 首页:显示留言列表和留言表单
@app.route('/')
def index():
    return render_template('index.html', messages=messages)

# 处理新增留言的POST请求
@app.route('/add_message', methods=['POST'])
def add_message():
    # 获取表单数据
    name = request.form.get('name', '').strip()
    content = request.form.get('content', '').strip()

    if name and content:
        # 添加留言到列表
        messages.append({
            'id': len(messages) + 1,  # 模拟ID
            'name': name,
            'content': content,
            'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        })
        # flash消息:显示一次就消失的提示
        flash('🎉 留言添加成功!', 'success')
    else:
        flash('⚠️ 昵称和留言内容不能为空!', 'error')

    # redirect(url_for('函数名'))跳转到指定路由,避免重复提交表单
    return redirect(url_for('index'))

# 处理删除留言的请求
@app.route('/delete/<int:msg_id>')
def delete_message(msg_id):
    global messages
    # 过滤掉要删除的留言
    messages = [msg for msg in messages if msg['id'] != msg_id]
    flash('🗑️ 留言删除成功!', 'info')
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)

3. 最后创建templates/index.html

<!-- templates/index.html -->
{% extends "base.html" %}

{% block title %}简易留言板{% endblock %}

{% block content %}
    <!-- 显示flash消息 -->
    {% with messages = get_flashed_messages(with_categories=true) %}
        {% if messages %}
            <div class="flash-messages">
                {% for category, msg in messages %}
                    <div class="flash {{ category }}">{{ msg }}</div>
                {% endfor %}
            </div>
        {% endif %}
    {% endwith %}

    <!-- 留言表单 -->
    <div class="form-box">
        <h3>留下你的足迹 ✍️</h3>
        <form action="{{ url_for('add_message') }}" method="post">
            <label for="name">昵称:</label>
            <input type="text" id="name" name="name" placeholder="请输入昵称" required>
            <br><br>
            <label for="content">留言:</label>
            <textarea id="content" name="content" rows="4" placeholder="请输入留言内容" required></textarea>
            <br><br>
            <button type="submit">发送</button>
        </form>
    </div>

    <!-- 留言列表 -->
    <div class="message-box">
        <h3>留言墙 📋</h3>
        {% if messages %}
            <ul class="message-list">
                {% for msg in messages %}
                    <li class="message-item">
                        <div class="msg-header">
                            <span class="msg-name">{{ msg.name }}</span>
                            <span class="msg-time">{{ msg.time }}</span>
                            <a href="{{ url_for('delete_message', msg_id=msg.id) }}" class="delete-btn">删除</a>
                        </div>
                        <div class="msg-content">{{ msg.content }}</div>
                    </li>
                {% endfor %}
            </ul>
        {% else %}
            <p class="no-msg">暂无留言,快来抢沙发吧!🛋️</p>
        {% endif %}
    </div>
{% endblock %}
在Python终端里执行下面的命令,生成一个随机的、安全的32位密钥: ```python import secrets print(secrets.token_hex(16)) ``` 把输出的结果替换掉`app.py`里的`app.secret_key`即可。

8. 相关教程

Flask的简洁性使其成为学习Web开发的绝佳入门选择!建议你: 1. 先把本文的留言板跑通,改改样式和功能试试; 2. 再学Flask的扩展(比如Flask-SQLAlchemy操作数据库); 3. 最后尝试做一个更完整的项目(比如个人博客、Todo List)。

总结

通过本文的学习,你已经掌握了Flask的全部核心基础

  1. 如何搭建Flask的开发环境(虚拟环境+安装Flask)
  2. 如何创建和运行第一个Flask应用
  3. 如何使用路由系统(基本路由、带变量的路由、HTTP方法)
  4. 如何使用Jinja2模板引擎(变量、条件、循环、继承)
  5. 如何管理静态文件
  6. 如何做一个完整的入门练手项目

Flask的灵活性和可扩展性使其适用于各种规模的Web应用,从简单的API到复杂的企业级应用都能胜任!接下来,你可以继续学习Flask的扩展,让你的应用更强大~