【Flask】一文掌握 Flask 基础用法

文章目录

    • [前言:为什么要学习 Flask?](#前言:为什么要学习 Flask?)
    • [一、Flask 的基石 - 核心对象与生命周期](#一、Flask 的基石 - 核心对象与生命周期)
      • [1.1 Flask 应用实例](#1.1 Flask 应用实例)
      • [1.2 应用配置](#1.2 应用配置)
    • [二、路由系统 - 请求的入口](#二、路由系统 - 请求的入口)
      • [2.1 基本路由定义](#2.1 基本路由定义)
      • [2.2 动态路由与变量规则](#2.2 动态路由与变量规则)
      • [2.3 HTTP 方法](#2.3 HTTP 方法)
      • [2.4 URL 构建 (`url_for`)](#2.4 URL 构建 (url_for))
    • [三、模板引擎 - Jinja2 的艺术](#三、模板引擎 - Jinja2 的艺术)
      • [3.1 渲染模板](#3.1 渲染模板)
      • [3.2 Jinja2 语法详解](#3.2 Jinja2 语法详解)
    • [四、请求与响应 - 与客户端的对话](#四、请求与响应 - 与客户端的对话)
      • [4.1 全局请求对象 (`request`)](#4.1 全局请求对象 (request))
      • [4.2 构建响应](#4.2 构建响应)
      • [4.3 重定向](#4.3 重定向)
    • [五、会话管理 - 记住用户的状态](#五、会话管理 - 记住用户的状态)
      • [5.1 使用 Session](#5.1 使用 Session)
    • [六、高级功能 - 蓝图与钩子](#六、高级功能 - 蓝图与钩子)
      • [6.1 蓝图 - 模块化应用](#6.1 蓝图 - 模块化应用)
      • [6.2 请求钩子](#6.2 请求钩子)
    • [七、Flask 扩展生态系统](#七、Flask 扩展生态系统)
      • [7.1 Flask-SQLAlchemy - 数据库 ORM](#7.1 Flask-SQLAlchemy - 数据库 ORM)
      • [7.2 Flask-Migrate - 数据库迁移](#7.2 Flask-Migrate - 数据库迁移)
      • [7.3 Flask-WTF - 表单处理与 CSRF 防护](#7.3 Flask-WTF - 表单处理与 CSRF 防护)
    • 八、部署
      • [8.1 WSGI的方式](#8.1 WSGI的方式)
      • [8.2 Gunicorn的方式](#8.2 Gunicorn的方式)
      • [8.3 Nginx的方式](#8.3 Nginx的方式)

前言:为什么要学习 Flask?

在 Python Web 开发的世界里,Django 和 Flask 是两座巍峨的高峰。Django 如同一座装备齐全的"全能战舰",自带 ORM、后台管理、表单系统等,适合快速构建功能复杂的大型项目。而 Flask 则像一艘性能卓越的"快艇",它只提供最核心的工具:路由和请求处理。这种"微"哲学带来了无与伦比的灵活性和自由度,我们可以按需选择最适合的数据库、表单库或其他组件,打造完全定制化的应用。

Flask 虽然被称为"微框架",但其内涵博大精深,其"微"体现在核心简洁,而强大的扩展性则赋予了它无限可能。

一、Flask 的基石 - 核心对象与生命周期

在编写任何 Flask 应用之前,理解其核心对象 Flask 以及应用的生命周期至关重要。

1.1 Flask 应用实例

这行代码是每个 Flask 应用的起点,它创建了一个 Flask 类的实例。

python 复制代码
from flask import Flask
app = Flask(__name__)
  • __name__ 参数的作用是什么?
    • 这是一个 Python 的特殊变量。当你的模块被直接运行时(如 python app.py),它的值是 '__main__'。当它被其他模块导入时,它的值是模块的名字(如 'app')。
    • Flask 利用这个值来确定应用的根目录,从而能够正确地找到 templatesstatic 文件夹的位置。例如,render_template('index.html') 会在 应用根目录/templates/ 下寻找文件。所以,通常情况下,总是传入 __name__

1.2 应用配置

Flask 的配置是一个类似字典的对象 app.config,用于存储应用运行时所需的各种配置变量。

python 复制代码
app = Flask(__name__)
# 设置配置
app.config['DEBUG'] = True  # 开启调试模式
app.config['SECRET_KEY'] = 'a-very-secret-key'  # 用于会话加密等
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db' # 数据库URI
# 从配置文件加载
# app.config.from_object('config.ProductionConfig')

常用配置项:

  • DEBUG: True/False。开启后,代码修改会自动重启服务,且发生错误时会在浏览器显示详细错误堆栈,开发时务必开启
  • SECRET_KEY: 一个长随机字符串。它对于 Flask 的会话管理、以及一些安全相关的扩展(如 Flask-WTF)至关重要,生产环境必须设置一个强密钥
  • TESTING: True/False。开启测试模式,会改变一些行为,如禁用错误捕获。
  • PERMANENT_SESSION_LIFETIME: 设置 session 的持久化时间,是一个 datetime.timedelta 对象。

二、路由系统 - 请求的入口

路由是 Web 框架的核心,它负责将用户访问的 URL 映射到相应的 Python 处理函数。

2.1 基本路由定义

使用 @app.route() 装饰器来定义路由。

python 复制代码
@app.route('/')
def index():
    return "这是首页"
@app.route('/hello')
def hello():
    return "Hello, Flask!"

2.2 动态路由与变量规则

这是 Flask 路由强大之处,可以在 URL 中捕获变量。

python 复制代码
# 默认类型是 string,接受任何不包含斜杠的文本
@app.route('/user/<username>')
def show_user_profile(username):
    return f'用户: {username}'
# 指定类型为 int,只接受整数
@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'文章 ID: {post_id}'
# 指定类型为 float,只接受浮点数
@app.route('/price/<float:price>')
def show_price(price):
    return f'价格: {price}'
# 指定类型为 path,接受包含斜杠的文本
@app.route('/files/<path:subpath>')
def show_subpath(subpath):
    return f'子路径: {subpath}'

支持的转换器类型:

  • string: (默认) 字符串
  • int: 整数
  • float: 浮点数
  • path: 路径,可以包含 /

2.3 HTTP 方法

Web 不仅仅是 GET 请求。@app.route() 装饰器可以接受一个 methods 参数来指定它响应的 HTTP 方法列表。

python 复制代码
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 处理登录逻辑
        return "处理登录请求"
    else:
        # 显示登录表单
        return "显示登录表单"
  • GET: 获取资源
  • POST: 创建新资源
  • PUT: 更新整个资源
  • PATCH: 部分更新资源
  • DELETE: 删除资源

2.4 URL 构建 (url_for)

在代码中硬编码 URL 是一个坏习惯。url_for() 函数可以根据视图函数名生成对应的 URL,这样可以避免因 URL 规则修改而导致大量代码改动。

python 复制代码
from flask import url_for
@app.route('/')
def index():
    # 生成 login 视图的 URL
    login_url = url_for('login')
    return f'<a href="{login_url}">去登录</a>'
@app.route('/login')
def login():
    return "登录页"

url_for 还可以处理动态路由:

python 复制代码
# url_for('show_user_profile', username='john_doe')
# 将生成 '/user/john_doe'

三、模板引擎 - Jinja2 的艺术

将业务逻辑和表现层分离是现代 Web 开发的最佳实践。Flask 默认集成了强大的 Jinja2 模板引擎。

3.1 渲染模板

使用 render_template() 函数来渲染模板。Flask 会在 templates 文件夹中寻找模板文件。

python 复制代码
from flask import render_template
@app.route('/profile/<name>')
def profile(name):
    # 将变量传递给模板
    return render_template('profile.html', name=name, age=30)

templates/profile.html:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>个人资料</title>
</head>
<body>
    <h1>你好, {{ name }}!</h1>
    <p>你的年龄是: {{ age }}</p>
</body>
</html>

3.2 Jinja2 语法详解

1、变量

使用 {``{ ... }} 来输出变量。

html 复制代码
<p>{{ my_variable }}</p>

Jinja2 可以访问 Python 对象的属性和方法:

html 复制代码
<p>{{ user.name }}</p>
<p>{{ user.get_name() }}</p>

2、控制结构 - 条件判断

使用 {% ... %} 来包裹控制语句。

html 复制代码
{% if user.is_authenticated %}
    <h1>欢迎回来, {{ user.username }}!</h1>
{% else %}
    <h1>请先登录</h1>
{% endif %}

3、控制结构 - 循环

html 复制代码
<ul>
    {% for post in posts %}
        <li>{{ post.title }}</li>
    {% else %}
        <li>没有找到任何文章。</li>
    {% endfor %}

在循环内部,你可以访问一些特殊变量:

  • loop.index: 当前迭代的索引(从1开始)
  • loop.index0: 当前迭代的索引(从0开始)
  • loop.first: 是否是第一次迭代
  • loop.last: 是否是最后一次迭代

4、模板继承

这是 Jinja2 最强大的功能之一,可以创建一个包含所有公共元素(如页头、页脚、导航栏)的"基础模板",然后让子模板继承并覆盖特定部分。
templates/base.html:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}我的网站{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <header>
        <h1>我的网站</h1>
        <nav>...</nav>
    </header>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>&copy; 2023 我的网站</p>
    </footer>
</body>
</html>

templates/child.html:

html 复制代码
{% extends "base.html" %}
{% block title %}
    首页 - {{ super() }}
{% endblock %}
{% block content %}
    <h2>欢迎来到首页!</h2>
    <p>这里是页面的主要内容。</p>
{% endblock %}
  • {% extends "base.html" %}: 声明继承自哪个模板。
  • {% block block_name %}...{% endblock %}: 定义一个可以被子模板覆盖的"块"。
  • {``{ super() }}: 在子模板块中调用父模板同名块的内容。

5、宏

宏类似于编程语言中的函数,用于定义可重用的 HTML 片段。
templates/macros.html:

html 复制代码
{% macro render_field(field) %}
  <div class="form-group">
    {{ field.label(class="form-control-label") }}
    {{ field(class="form-control") }}
    {% if field.errors %}
      <div class="invalid-feedback">
        {% for error in field.errors %}
          <span>{{ error }}</span>
        {% endfor %}
      </div>
    {% endif %}
  </div>
{% endmacro %}

在其他模板中使用:

html 复制代码
{% from 'macros.html' import render_field %}
<form method="post">
    {{ render_field(form.username) }}
    {{ render_field(form.password) }}
    <input type="submit" value="Submit">
</form>

四、请求与响应 - 与客户端的对话

Web 应用的本质就是处理请求并返回响应。

4.1 全局请求对象 (request)

request 对象包含了客户端发出的 HTTP 请求的全部信息。注意:request 是一个代理对象,只在请求上下文中有效。

python 复制代码
from flask import request
@app.route('/search')
def search():
    # 获取查询字符串参数 ?q=flask
    query = request.args.get('q', '') # .get(key, default) 方法更安全
    return f'你搜索的关键词是: {query}'
@app.route('/login', methods=['POST'])
def do_login():
    # 获取表单数据
    username = request.form['username']
    password = request.form['password']
    # 获取 JSON 数据
    # data = request.get_json()
    return f'用户 {username} 尝试登录'

request 常用属性:

  • request.method: HTTP 方法 ('GET', 'POST' 等)
  • request.form: 包含 POST 或 PUT 请求中的表单数据的字典。
  • request.args: 包含 URL 查询字符串参数的字典。
  • request.values: formargs 的合集。
  • request.files: 包含上传文件的字典。
  • request.cookies: 包含客户端 cookies 的字典。
  • request.headers: 包含 HTTP 头的字典。
  • request.get_json(): 解析请求体中的 JSON 数据,返回 Python 字典。

4.2 构建响应

视图函数的返回值会自动被 Flask 转换为一个响应对象。

  • 返回字符串 : Flask 会将其包装成一个以字符串为体、状态码为 200 OK、MIME 类型为 text/html 的响应。

  • 返回元组 : (response, status, headers)(response, status)

    python 复制代码
    @app.route('/not-found')
    def not_found():
        return '页面未找到', 404
    @app.route('/api/data')
    def api_data():
        return {'key': 'value'}, 200, {'X-Custom-Header': 'SomeValue'}
  • 使用 make_response : 创建一个 Response 对象,可以进行更精细的控制。

    python 复制代码
    from flask import make_response
    @app.route('/cookie')
    def set_cookie():
        resp = make_response('设置 Cookie 成功')
        resp.set_cookie('username', 'john')
        return resp
  • 返回 JSON : 使用 jsonify 函数,它会设置正确的 Content-Type 头 (application/json)。

    python 复制代码
    from flask import jsonify
    @app.route('/api/user')
    def get_user():
        return jsonify(username='john', email='john@example.com')

4.3 重定向

使用 redirect 函数可以将用户重定向到另一个 URL。

python 复制代码
from flask import redirect, url_for
@app.route('/old-page')
def old_page():
    # 重定向到 new_page 视图
    return redirect(url_for('new_page'))
@app.route('/new-page')
def new_page():
    return "这是新页面"

五、会话管理 - 记住用户的状态

HTTP 是无状态的,session(会话)技术是解决这个问题的关键,它允许我们在不同请求之间存储用户特定信息。

Flask 的 Session 是基于 客户端签名 Cookie 实现的。这意味着所有会话数据都经过加密签名后存储在用户的浏览器中。优点是无需服务端存储,缺点是不能存储敏感数据,且有大小限制。

5.1 使用 Session

python 复制代码
from flask import session
# 必须设置 SECRET_KEY
app.config['SECRET_KEY'] = 'your-secret-key-here'
@app.route('/set_session')
def set_session():
    session['username'] = 'testuser'
    session['user_id'] = 123
    return 'Session 已设置'
@app.route('/get_session')
def get_session():
    username = session.get('username', 'Guest')
    user_id = session.get('user_id')
    return f'用户: {username}, ID: {user_id}'
@app.route('/clear_session')
def clear_session():
    # 清除整个会话
    session.clear()
    return 'Session 已清除'

关键点:

  • session 对象的操作就像一个字典。
  • 必须设置 app.config['SECRET_KEY'],否则会抛出异常。
  • 默认情况下,session cookie 在浏览器关闭后失效。要使其持久化,设置 session.permanent = True,并配置 PERMANENT_SESSION_LIFETIME

六、高级功能 - 蓝图与钩子

当应用规模增长时,需要更高级的组织方式和执行机制。

6.1 蓝图 - 模块化应用

蓝图是组织一组相关视图和代码的方式。它们不是独立的应用,但可以在应用注册后,以应用的一部分来运行。这对于大型项目或团队协作至关重要。
项目结构示例:

复制代码
/myapp
    /app
        __init__.py
        /auth
            __init__.py
            routes.py
        /blog
            __init__.py
            routes.py
    run.py

app/auth/__init__.py (定义蓝图):

python 复制代码
from flask import Blueprint
# 创建一个蓝图实例
# 第一个参数是蓝图的名字
# 第二个参数是 __name__,用于定位蓝图资源
# url_prefix 会给该蓝图下的所有路由加上前缀
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
from . import routes # 导入路由,避免循环依赖

app/auth/routes.py (在蓝图上定义路由):

python 复制代码
from flask import render_template
from . import auth_bp
@auth_bp.route('/login')
def login():
    return render_template('auth/login.html')
@auth_bp.route('/register')
def register():
    return render_template('auth/register.html')

app/__init__.py (注册蓝图):

python 复制代码
from flask import Flask
def create_app():
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'a-very-secret-key'
    # ... 其他配置 ...
    # 注册蓝图
    from .auth import auth_bp
    app.register_blueprint(auth_bp)
    from .blog import blog_bp
    app.register_blueprint(blog_bp)
    return app

run.py (启动应用):

python 复制代码
from app import create_app
app = create_app()
if __name__ == '__main__':
    app.run(debug=True)

现在,登录页面的 URL 就变成了 /auth/login

6.2 请求钩子

请求钩子是在处理请求之前或之后执行的函数,非常适合用于权限验证、打开数据库连接、性能监控等场景。

  • @before_request: 在每个请求之前执行。如果返回了响应,则直接停止,不再执行视图函数。
  • @before_first_request: 在处理第一个 请求之前执行。只执行一次。可用于初始化操作。
  • @after_request: 在每个请求之后执行,前提是没有未处理的异常。它需要接收并返回响应对象。
  • @teardown_request: 在每个请求之后执行,无论是否出现异常。它接收异常对象作为参数。
python 复制代码
@app.before_request
def before_request():
    # 检查用户是否登录
    if 'user_id' not in session and request.endpoint not in ['login', 'static']:
        return redirect(url_for('login'))
@app.after_request
def after_request(response):
    # 在响应头中添加一个自定义头
    response.headers['X-Frame-Options'] = 'SAMEORIGIN'
    return response
@app.teardown_request
def teardown_request(exception):
    # 关闭数据库连接等清理工作
    db.close()

七、Flask 扩展生态系统

Flask 的"微"魅力在于其丰富的扩展。以下是一些最常用和最重要的扩展。

7.1 Flask-SQLAlchemy - 数据库 ORM

SQLAlchemy 是 Python 中最强大的 ORM 框架。Flask-SQLAlchemy 为其提供了 Flask 集成。
安装 : pip install Flask-SQLAlchemy
配置与使用:

python 复制代码
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.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(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    def __repr__(self):
        return f"User('{self.username}', '{self.email}')"
# 在视图或命令行中使用
# db.create_all() # 创建表
# user = User(username='test', email='test@example.com')
# db.session.add(user)
# db.session.commit()
# User.query.all()
# User.query.filter_by(username='test').first()

7.2 Flask-Migrate - 数据库迁移

在开发过程中,数据库模型经常变化。Flask-Migrate(基于 Alembic)可以管理数据库结构的版本控制,实现平滑升级和降级。
安装 : pip install Flask-Migrate
使用:

python 复制代码
from flask_migrate import Migrate
# ... 在 create_app() 中 ...
db = SQLAlchemy(app)
migrate = Migrate(app, db)

命令行操作:

bash 复制代码
# 初始化迁移环境 (只需一次)
flask db init
# 生成迁移脚本 (检测模型变化)
flask db migrate -m "Initial migration."
# 应用迁移到数据库
flask db upgrade

7.3 Flask-WTF - 表单处理与 CSRF 防护

Web 表单处理繁琐且容易出错。Flask-WTF 集成了 WTForms 库,简化了表单定义、验证和渲染,并内置了 CSRF 防护。
安装 : pip install Flask-WTF
使用:

python 复制代码
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=4, max=20)])
    password = PasswordField('密码', validators=[DataRequired()])
    submit = SubmitField('登录')
# 在视图中
@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit(): # 验证表单数据
        # 登录逻辑
        return redirect(url_for('index'))
    return render_template('login.html', title='登录', form=form)

模板渲染 (login.html):

html 复制代码
{% from "bootstrap/wtf.html" import render_field %} <!-- 如果使用Flask-Bootstrap -->
<form method="POST" action="">
    {{ form.hidden_tag() }} <!-- CSRF Token -->
    {{ render_field(form.username) }}
    {{ render_field(form.password) }}
    {{ form.submit() }}
</form>

八、部署

开发服务器 (app.run()) 性能差且不稳定,绝不能用于生产环境。生产部署通常使用专业的 WSGI 服务器,如 Gunicorn 或 uWSGI,配合反向代理 Nginx。

8.1 WSGI的方式

WSGI (Web Server Gateway Interface) 是 Python Web 应用和 Web 服务器之间的通用接口标准。Flask 应用就是一个 WSGI 应用。

8.2 Gunicorn的方式

一个流行的 Python WSGI HTTP 服务器。
安装 : pip install gunicorn
运行:

bash 复制代码
# -w 4 表示启动 4 个 worker 进程
# -b 0.0.0.0:8000 表示绑定到所有网络接口的 8000 端口
# myproject:app 是 模块名:Flask实例名
gunicorn -w 4 -b 0.0.0.0:8000 myproject:app

8.3 Nginx的方式

作为反向代理,Nginx 可以处理静态文件、负载均衡、SSL 终止和 HTTP 缓存,将动态请求转发给 Gunicorn。
Nginx 配置示例 (/etc/nginx/sites-available/myproject):

nginx 复制代码
server {
    listen 80;
    server_name your_domain.com;
    location /static {
        alias /path/to/your/project/static; # 静态文件路径
    }
    location / {
        proxy_pass http://127.0.0.1:8000; # 转发给 Gunicorn
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
相关推荐
q***73551 小时前
windows配置永久路由
android·前端·后端
g***72701 小时前
【MySQL】数据库和表的操作
数据库·mysql·oracle
zyplayer-doc1 小时前
目录支持批量操作,文档增加可见范围、锁定功能,PDF查看优化,zyplayer-doc 2.5.8 发布啦!
数据库·人工智能·pdf·编辑器·飞书·石墨文档
码事漫谈1 小时前
不懂汇编的后端不是一个好的开发
后端
码事漫谈1 小时前
AI时代,汇编语言还有必要学吗?我的惨痛教训与思考
后端
Mr数据杨2 小时前
【Gradio】Gradio 启动规避 Huggingface 代理问题
python·gradio
“αβ”2 小时前
MySQL库的操作
linux·服务器·网络·数据库·c++·mysql·oracle
s***87272 小时前
【Python】网络爬虫——词云wordcloud详细教程,爬取豆瓣最新评论并生成各式词云
爬虫·python·信息可视化
考虑考虑2 小时前
jpa将SQL记录到日志文件
spring boot·后端·spring