导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • Runnable
  • PromptEngineering
  • dataclasses
  • LaTeX
  • rank_bm25
  • TF-IDF
  • asyncio
  • sqlalchemy
  • fastapi
  • Starlette
  • uvicorn
  • argparse
  • Generic
  • ssl
  • urllib
  • python-dotenv
  • RRF
  • CrossEncoder
  • Lost-in-the-middle
  • Jinja2
  • logger
  • io
  • venv
  • concurrent
  • parameter
  • SSE
  • 1. 什么是Flask?
    • 1.1 生活中的类比
    • 1.2 Flask的核心定位
    • 1.3 为什么选择Flask?
  • 2. Flask核心概念
    • 2.1 应用对象(Flask)
    • 2.2 路由(Route)
    • 2.3 视图函数
    • 2.4 请求对象(request)
    • 2.5 g 对象(全局变量)
    • 2.6 模板渲染(render_template)
    • 2.7 模板上下文处理器(context_processor)
    • 2.8 Flash Messages(闪存消息)
  • 3. 应用配置
    • 3.1 为什么需要配置管理?
    • 3.2 基本配置方式
    • 3.3 使用配置类(推荐方式)
    • 3.4 app.config.from_object()
    • 3.5 多环境配置管理
    • 3.6 配置的优先级
    • 3.7 配置的最佳实践
  • 4. 返回JSON数据(API)
    • 5.1 简单的API示例
    • 5.2 测试API
  • 6. 组织大型应用:Blueprint
    • 6.1 什么是Blueprint?
    • 6.2 为什么需要Blueprint?
    • 6.3 创建第一个Blueprint
    • 6.4 多个Blueprint示例
    • 6.5 Blueprint的URL前缀和模板
    • 6.6 Blueprint之间的通信
    • 6.7 Blueprint的最佳实践
    • 6.8 Blueprint的常见用法总结
  • 7. 静态文件
    • 7.1 项目结构
    • 7.2 使用静态文件
  • 8. 常见问题和注意事项
    • 8.1 调试模式
    • 8.2 端口被占用
    • 8.3 模板文件找不到
    • 8.4 静态文件找不到

1. 什么是Flask? #

Flask 是一个用 Python 编写的轻量级 Web 应用框架。它被设计为易于使用、灵活且可扩展,是构建 Web 应用程序和 API 的绝佳选择,尤其适合初学者和小型到中型项目。

1.1 生活中的类比 #

想象一下,你想建一个网站,比如个人博客或者在线商店。

传统方法:

  • 需要学习复杂的Web服务器配置
  • 需要处理HTTP协议的细节
  • 需要自己实现路由、模板等功能
  • 开发效率低,容易出错

使用Flask:

  • 几行代码就能创建一个Web应用
  • Flask帮你处理了底层的复杂性
  • 专注于业务逻辑,而不是技术细节
  • 开发效率高,代码简洁

Flask 就像是一个"工具箱",提供了构建Web应用所需的基本工具,让你可以快速搭建网站。

1.2 Flask的核心定位 #

Flask 的定位是:一个轻量级、灵活、易扩展的Web应用框架。

核心特点:

  • 微框架:核心简单,只提供最基本的功能
  • 高度可扩展:通过扩展库添加更多功能
  • 灵活自由:没有强制的项目结构
  • 易于学习:对初学者非常友好

1.3 为什么选择Flask? #

vs 传统Web开发:

  • 传统方式需要处理大量底层细节,Flask封装了这些复杂性
  • 传统方式开发效率低,Flask可以快速开发
  • 传统方式代码冗长,Flask代码简洁优雅

2. Flask核心概念 #

2.1 应用对象(Flask) #

Flask应用的核心是应用对象,它是整个Web应用的入口。

创建应用对象:

# 导入Flask类
# Flask类用于创建Web应用实例
from flask import Flask

# 创建Flask应用实例
# __name__用于确定应用的根路径,Flask会根据它找到模板和静态文件
app = Flask(__name__)

# 打印应用对象信息(用于验证)
print(f"Flask应用已创建: {app}")
print(f"应用名称: {app.name}")

应用对象的作用:

  • 配置路由(URL到函数的映射)
  • 注册扩展(如数据库、表单等)
  • 管理应用配置
  • 处理请求和响应

2.2 路由(Route) #

路由是将URL映射到Python函数的过程。当用户访问某个URL时,Flask会调用对应的函数。

基本路由:

# 导入Flask类
from flask import Flask

# 创建Flask应用实例
app = Flask(__name__)

# 定义根路由
# @app.route('/')表示当用户访问网站根目录时
# 装饰器@app.route()将URL '/' 映射到下面的函数
@app.route('/')
def index():
    # 返回响应内容(HTML字符串)
    return '<h1>欢迎访问首页!</h1>'

# 定义另一个路由
# '/about'表示访问 http://localhost:5000/about 时
@app.route('/about')
def about():
    # 返回关于页面的内容
    return '<h1>关于我们</h1><p>这是一个Flask应用示例。</p>'

# 运行应用
if __name__ == '__main__':
    # 启动开发服务器
    # host='0.0.0.0'表示监听所有网络接口
    # port=5000表示使用5000端口
    # debug=True开启调试模式
    app.run(host='0.0.0.0', port=5000, debug=True)

动态路由:

# 导入Flask类
from flask import Flask

# 创建Flask应用实例
app = Flask(__name__)

# 定义动态路由
# <name>是URL参数,会被传递给函数
# 访问 /user/张三 时,name='张三'
@app.route('/user/<name>')
def show_user(name):
    # 使用URL参数生成响应
    return f'<h1>你好,{name}!</h1>'

# 定义带类型的动态路由
# <int:post_id>表示post_id必须是整数
# 访问 /post/123 时,post_id=123(整数)
@app.route('/post/<int:post_id>')
def show_post(post_id):
    # 使用整数参数
    return f'<h1>文章 #{post_id}</h1>'

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

2.3 视图函数 #

视图函数是处理请求的核心,它接收请求,处理业务逻辑,然后返回响应。

视图函数的特点:

  • 必须返回一个响应(字符串、HTML、JSON等)
  • 可以访问请求数据(如表单数据、URL参数等)
  • 可以调用其他函数或访问数据库

示例:

# 导入Flask类和request对象
# request对象包含客户端发送的请求信息
from flask import Flask, request

# 创建Flask应用实例
app = Flask(__name__)

# 定义视图函数
# 这个函数处理根路径的请求
@app.route('/')
def index():
    # 返回简单的HTML响应
    return '<h1>首页</h1><p>欢迎访问!</p>'

# 定义带参数的视图函数
# 从URL中获取参数
@app.route('/greet/<name>')
def greet(name):
    # 使用URL参数生成响应
    return f'<h1>你好,{name}!</h1>'

# 定义处理GET和POST请求的视图函数
# methods参数指定允许的HTTP方法
@app.route('/form', methods=['GET', 'POST'])
def handle_form():
    # 判断请求方法
    if request.method == 'POST':
        # 如果是POST请求,获取表单数据
        # request.form是包含表单数据的字典
        username = request.form.get('username', '')
        return f'<h1>收到表单数据:{username}</h1>'
    else:
        # 如果是GET请求,返回表单页面
        return '''
        <form method="post">
            <label>用户名:</label>
            <input type="text" name="username">
            <button type="submit">提交</button>
        </form>
        '''

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

2.4 请求对象(request) #

request对象包含客户端发送的所有请求信息,如表单数据、URL参数、请求头等。

获取请求数据:

# 导入Flask类和request对象
from flask import Flask, request

# 创建Flask应用实例
app = Flask(__name__)

# 定义处理表单的路由
# methods=['GET', 'POST']表示支持GET和POST两种请求方法
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 判断请求方法
    if request.method == 'POST':
        # 获取表单数据
        # request.form是包含表单数据的字典
        username = request.form.get('username', '')
        password = request.form.get('password', '')

        # 简单的验证逻辑(实际应用中应该连接数据库)
        if username == 'admin' and password == '123456':
            return f'<h1>登录成功!</h1><p>欢迎,{username}!</p>'
        else:
            return '<h1>登录失败</h1><p>用户名或密码错误</p>'
    else:
        # GET请求,显示登录表单
        return '''
        <!DOCTYPE html>
        <html>
        <head>
            <title>登录</title>
        </head>
        <body>
            <h1>用户登录</h1>
            <form method="post">
                <label>用户名:</label><br>
                <input type="text" name="username" required><br><br>
                <label>密码:</label><br>
                <input type="password" name="password" required><br><br>
                <button type="submit">登录</button>
            </form>
        </body>
        </html>
        '''

# 定义获取URL参数的路由
# 访问 /search?q=Flask 时,q='Flask'
@app.route('/search')
def search():
    # 获取URL参数
    # request.args是包含URL参数的字典
    query = request.args.get('q', '')
    if query:
        return f'<h1>搜索结果</h1><p>搜索关键词:{query}</p>'
    else:
        return '<h1>请输入搜索关键词</h1>'

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

2.5 g 对象(全局变量) #

g 对象是 Flask 提供的请求级别的全局变量,用于在同一个请求的多个函数之间共享数据。它在每个请求开始时创建,请求结束时自动清除。

为什么需要 g 对象?

问题场景:

  • 在视图函数中需要调用多个辅助函数
  • 这些辅助函数都需要访问相同的数据(如当前用户、数据库连接等)
  • 如果每次都通过参数传递会很繁琐

传统方式的问题:

# 不好的做法:通过参数传递
def get_user_posts(user_id):
    # 需要数据库连接
    pass

def get_user_comments(user_id):
    # 也需要数据库连接
    pass

@app.route('/user/<user_id>')
def show_user(user_id):
    db = get_database_connection()  # 获取数据库连接
    posts = get_user_posts(user_id, db)  # 需要传递db
    comments = get_user_comments(user_id, db)  # 需要传递db
    return render_template('user.html', posts=posts, comments=comments)

使用 g 对象的解决方案:

# 好的做法:使用 g 对象
from flask import g

def get_user_posts(user_id):
    # 直接从 g 对象获取数据库连接
    posts = g.db.query(...).filter_by(user_id=user_id).all()
    return posts

@app.route('/user/<user_id>')
def show_user(user_id):
    g.db = get_database_connection()  # 存储到 g 对象
    posts = get_user_posts(user_id)  # 不需要传递db
    comments = get_user_comments(user_id)  # 不需要传递db
    return render_template('user.html', posts=posts, comments=comments)

基本用法:

# 导入Flask类和g对象
# g对象是Flask提供的请求级别的全局变量
from flask import Flask, g, render_template

# 创建Flask应用实例
app = Flask(__name__)

# 模拟获取当前用户信息的函数
def get_current_user():
    # 在实际应用中,这里可能从session或token中获取用户信息
    # 这里只是模拟
    return {'id': 1, 'name': '张三', 'role': 'admin'}

# 定义在请求开始前执行的函数
# before_request装饰器会在每个请求处理前执行
@app.before_request
def load_user():
    # 将当前用户信息存储到g对象中
    # 这样在同一个请求的所有函数中都可以访问
    g.current_user = get_current_user()
    # 可以存储多个值
    g.request_start_time = time.time()

# 定义辅助函数,从g对象获取数据
def get_user_name():
    # 从g对象获取当前用户信息
    # 如果g对象中没有该属性,会抛出AttributeError
    return g.current_user['name']

def is_admin():
    # 检查当前用户是否是管理员
    return g.current_user.get('role') == 'admin'

# 定义路由
@app.route('/')
def index():
    # 在视图函数中可以直接使用g对象
    user_name = g.current_user['name']
    # 也可以调用辅助函数
    is_user_admin = is_admin()

    return f'''
    <h1>欢迎,{user_name}!</h1>
    <p>管理员权限:{'是' if is_user_admin else '否'}</p>
    <p>请求开始时间:{g.request_start_time}</p>
    '''

# 定义另一个路由,也可以访问g对象
@app.route('/profile')
def profile():
    # 在这个路由中也可以访问g对象中的用户信息
    user = g.current_user
    return f'''
    <h1>用户资料</h1>
    <p>用户ID:{user["id"]}</p>
    <p>用户名:{user["name"]}</p>
    <p>角色:{user["role"]}</p>
    '''

# 运行应用
if __name__ == '__main__':
    import time
    app.run(debug=True)

使用 g 对象存储数据库连接:

# 导入Flask类和g对象
from flask import Flask, g, render_template
import sqlite3

# 创建Flask应用实例
app = Flask(__name__)

# 数据库文件路径
DATABASE = 'example.db'

# 获取数据库连接的函数
def get_db():
    # 如果g对象中已经有数据库连接,直接返回
    # 这样可以避免在同一个请求中重复创建连接
    db = getattr(g, '_database', None)
    if db is None:
        # 如果g对象中没有连接,创建新连接
        db = g._database = sqlite3.connect(DATABASE)
        # 设置返回字典格式的游标(更易用)
        db.row_factory = sqlite3.Row
    return db

# 定义在请求结束时关闭数据库连接的函数
# teardown_appcontext装饰器会在请求结束时执行
@app.teardown_appcontext
def close_db(error):
    # 关闭数据库连接
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

# 定义查询用户的函数
def get_user_by_id(user_id):
    # 从g对象获取数据库连接
    db = get_db()
    # 执行查询
    cursor = db.execute('SELECT * FROM users WHERE id = ?', (user_id,))
    user = cursor.fetchone()
    return dict(user) if user else None

# 定义路由
@app.route('/user/<int:user_id>')
def show_user(user_id):
    # 使用辅助函数查询用户
    # 辅助函数内部会从g对象获取数据库连接
    user = get_user_by_id(user_id)
    if user:
        return f'''
        <h1>用户信息</h1>
        <p>ID:{user["id"]}</p>
        <p>姓名:{user["name"]}</p>
        <p>邮箱:{user["email"]}</p>
        '''
    else:
        return '<h1>用户不存在</h1>', 404

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

使用 g 对象存储请求级别的缓存:

# 导入Flask类和g对象
from flask import Flask, g, render_template
import time

# 创建Flask应用实例
app = Flask(__name__)

# 模拟一个耗时的数据获取函数
def fetch_expensive_data():
    # 模拟耗时操作(实际应用中可能是数据库查询、API调用等)
    time.sleep(0.1)
    return {'data': '这是耗时获取的数据', 'timestamp': time.time()}

# 定义在请求开始前执行的函数
@app.before_request
def load_data():
    # 将耗时获取的数据存储到g对象中
    # 这样在同一个请求的多个函数中都可以使用,避免重复获取
    g.expensive_data = fetch_expensive_data()

# 定义辅助函数
def get_data():
    # 从g对象获取数据
    return g.expensive_data

# 定义路由1
@app.route('/page1')
def page1():
    # 使用g对象中的数据
    data = g.expensive_data
    return f'<h1>页面1</h1><p>数据:{data["data"]}</p>'

# 定义路由2
@app.route('/page2')
def page2():
    # 也可以使用辅助函数获取数据
    data = get_data()
    return f'<h1>页面2</h1><p>数据:{data["data"]}</p>'

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

实际应用示例:用户认证和权限检查:

# 导入Flask类和所需函数
from flask import Flask, g, render_template, request, session, redirect, url_for

# 创建Flask应用实例
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'

# 模拟用户数据库(实际应用中来自真实数据库)
users_db = {
    'admin': {'password': '123456', 'role': 'admin', 'name': '管理员'},
    'user1': {'password': 'password', 'role': 'user', 'name': '普通用户'}
}

# 定义在请求开始前加载当前用户的函数
@app.before_request
def load_current_user():
    # 从session中获取用户名
    username = session.get('username')
    if username and username in users_db:
        # 如果用户已登录,将用户信息存储到g对象
        user_info = users_db[username]
        g.current_user = {
            'username': username,
            'name': user_info['name'],
            'role': user_info['role']
        }
    else:
        # 如果用户未登录,g.current_user为None
        g.current_user = None

# 定义检查用户是否登录的辅助函数
def is_logged_in():
    # 检查g对象中是否有当前用户
    return g.current_user is not None

# 定义检查用户角色的辅助函数
def is_admin():
    # 检查当前用户是否是管理员
    return g.current_user and g.current_user['role'] == 'admin'

# 定义需要登录才能访问的装饰器
def login_required(f):
    from functools import wraps
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # 检查用户是否登录
        if not is_logged_in():
            # 如果未登录,重定向到登录页面
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

# 定义需要管理员权限的装饰器
def admin_required(f):
    from functools import wraps
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # 检查用户是否登录
        if not is_logged_in():
            return redirect(url_for('login'))
        # 检查用户是否是管理员
        if not is_admin():
            return '<h1>权限不足</h1><p>您需要管理员权限才能访问此页面。</p>', 403
        return f(*args, **kwargs)
    return decorated_function

# 定义登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 如果已登录,重定向到首页
    if is_logged_in():
        return redirect(url_for('index'))

    if request.method == 'POST':
        username = request.form.get('username', '').strip()
        password = request.form.get('password', '')

        # 验证用户名和密码
        if username in users_db and users_db[username]['password'] == password:
            # 登录成功,存储用户名到session
            session['username'] = username
            # 重定向到首页(g对象会在下一个请求中自动更新)
            return redirect(url_for('index'))
        else:
            return '<h1>登录失败</h1><p>用户名或密码错误。</p>'
    else:
        # GET请求,显示登录表单
        return '''
        <form method="post">
            <label>用户名:</label>
            <input type="text" name="username" required><br><br>
            <label>密码:</label>
            <input type="password" name="password" required><br><br>
            <button type="submit">登录</button>
        </form>
        '''

# 定义登出路由
@app.route('/logout')
def logout():
    # 清除session
    session.pop('username', None)
    return redirect(url_for('index'))

# 定义首页路由(需要登录)
@app.route('/')
@login_required
def index():
    # 使用g对象中的当前用户信息
    user = g.current_user
    return f'''
    <h1>欢迎,{user["name"]}!</h1>
    <p>用户名:{user["username"]}</p>
    <p>角色:{user["role"]}</p>
    <a href="/profile">查看资料</a>
    <a href="/admin">管理后台</a>
    <a href="/logout">登出</a>
    '''

# 定义用户资料路由(需要登录)
@app.route('/profile')
@login_required
def profile():
    # 使用g对象中的当前用户信息
    user = g.current_user
    return f'''
    <h1>用户资料</h1>
    <p>用户名:{user["username"]}</p>
    <p>姓名:{user["name"]}</p>
    <p>角色:{user["role"]}</p>
    <a href="/">返回首页</a>
    '''

# 定义管理后台路由(需要管理员权限)
@app.route('/admin')
@admin_required
def admin():
    # 只有管理员才能访问
    # 使用g对象中的当前用户信息
    user = g.current_user
    return f'''
    <h1>管理后台</h1>
    <p>欢迎,{user["name"]}管理员!</p>
    <p>这里是管理后台的内容。</p>
    <a href="/">返回首页</a>
    '''

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

注意事项:

  1. 请求级别:g 对象只在同一个请求中有效,不同请求之间的 g 对象是独立的
  2. 自动清除:每个请求结束后,g 对象会自动清除,不需要手动清理
  3. 线程安全:Flask 确保每个请求的 g 对象是线程安全的
  4. 属性访问:访问不存在的属性会抛出 AttributeError,建议使用 getattr(g, 'key', default) 或先检查

最佳实践:

  1. 使用 before_request:在 `@app.before_request` 中初始化 g 对象的数据
  2. 使用 teardown_appcontext:在 `@app.teardown_appcontext` 中清理资源(如关闭数据库连接)
  3. 避免存储大量数据:g 对象应该只存储请求级别需要的小量数据
  4. 使用辅助函数:通过辅助函数访问 g 对象,而不是直接访问,提高代码可维护性

2.6 模板渲染(render_template) #

直接在Python代码中拼接HTML字符串很繁琐,Flask使用Jinja2模板引擎来分离Python代码和HTML。

为什么需要模板?

  • HTML代码和Python代码分离,更易维护
  • 可以复用HTML结构(如导航栏、页脚)
  • 支持变量、循环、条件判断等

使用模板:

首先创建模板文件 templates/hello.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>问候页面</title>
</head>
<body>
    <h1>你好,{{ name }}!</h1>
    <p>欢迎访问我们的网站。</p>
</body>
</html>

然后使用模板:

# 导入Flask类和render_template函数
# render_template用于渲染HTML模板
from flask import Flask, render_template

# 创建Flask应用实例
app = Flask(__name__)

# 定义路由,使用模板渲染
# <name>是URL参数
@app.route('/hello/<name>')
def hello(name):
    # 渲染模板
    # 'hello.html'是模板文件名(在templates目录下)
    # name=name将Python变量传递给模板
    return render_template('hello.html', name=name)

# 定义带列表数据的路由
@app.route('/users')
def show_users():
    # 定义用户列表
    users = ['张三', '李四', '王五']
    # 将用户列表传递给模板
    return render_template('users.html', users=users)

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

创建 templates/users.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户列表</title>
</head>
<body>
    <h1>用户列表</h1>
    <ul>
        {% for user in users %}
        <li>{{ user }}</li>
        {% endfor %}
    </ul>
</body>
</html>

2.7 模板上下文处理器(context_processor) #

在多个模板中都需要使用相同的变量时(如用户信息、网站配置等),如果每个视图函数都传递这些变量会很繁琐。Flask提供了`@app.context_processor`装饰器,可以向所有模板的上下文注入全局变量。

为什么需要context_processor?

问题场景:

  • 每个模板都需要显示当前登录用户信息
  • 每个模板都需要显示网站标题、导航菜单等
  • 需要在所有模板中使用某些工具函数

传统方式的问题:

# 每个视图函数都需要传递相同的变量
@app.route('/')
def index():
    current_user = get_current_user()  # 获取当前用户
    site_title = '我的网站'
    return render_template('index.html', 
                         current_user=current_user, 
                         site_title=site_title)

@app.route('/about')
def about():
    current_user = get_current_user()  # 重复代码
    site_title = '我的网站'  # 重复代码
    return render_template('about.html', 
                         current_user=current_user, 
                         site_title=site_title)

使用context_processor的解决方案:

# 定义一次,所有模板都可以使用
@app.context_processor
def inject_user():
    return dict(current_user=get_current_user(), 
                site_title='我的网站')

基本用法:

# 导入Flask类和render_template函数
from flask import Flask, render_template

# 创建Flask应用实例
app = Flask(__name__)

# 定义context_processor函数
# 这个函数会在每次渲染模板时自动调用
# 返回的字典中的变量会被注入到所有模板的上下文中
@app.context_processor
def inject_global_vars():
    # 返回一个字典,字典中的键值对会成为模板中的全局变量
    return {
        'site_name': '我的Flask网站',
        'site_url': 'https://example.com',
        'current_year': 2024
    }

# 定义路由
@app.route('/')
def index():
    # 不需要传递site_name等变量,它们已经在模板上下文中了
    return render_template('index.html')

@app.route('/about')
def about():
    # 同样不需要传递,所有模板都可以直接使用
    return render_template('about.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

在模板中使用:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{{ site_name }}</title>
</head>
<body>
    <h1>欢迎访问 {{ site_name }}</h1>
    <p>网站地址:{{ site_url }}</p>
    <p>当前年份:{{ current_year }}</p>
</body>
</html>

注入函数:

context_processor不仅可以注入变量,还可以注入函数,让模板中可以使用Python函数。

from flask import Flask, render_template
from datetime import datetime

app = Flask(__name__)

# 定义一些工具函数
def format_date(date):
    """格式化日期"""
    return date.strftime('%Y年%m月%d日')

def get_greeting():
    """根据时间返回问候语"""
    hour = datetime.now().hour
    if hour < 12:
        return '早上好'
    elif hour < 18:
        return '下午好'
    else:
        return '晚上好'

# 将函数注入到模板上下文
@app.context_processor
def inject_helpers():
    return {
        'format_date': format_date,  # 注入格式化函数
        'greeting': get_greeting()    # 注入问候语(调用函数获取值)
    }

# 定义路由
@app.route('/')
def index():
    return render_template('index.html', 
                         current_date=datetime.now())

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

在模板中使用函数:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>{{ greeting }}!</h1>
    <p>当前日期:{{ format_date(current_date) }}</p>
    <!-- format_date函数可以在模板中直接调用 -->
</body>
</html>

注入当前用户信息:

实际应用中最常见的用法是注入当前登录用户信息。

from flask import Flask, render_template, session

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'

# 模拟用户数据(实际应用中来自数据库)
users = {
    'admin': {'name': '管理员', 'email': 'admin@example.com'},
    'user1': {'name': '用户1', 'email': 'user1@example.com'}
}

def get_current_user():
    """获取当前登录用户"""
    # 从session中获取用户名
    username = session.get('username')
    if username and username in users:
        return users[username]
    return None

# 将当前用户注入到所有模板
@app.context_processor
def inject_user():
    return {
        'current_user': get_current_user()  # 每次渲染模板时都会获取最新用户信息
    }

# 登录路由
@app.route('/login/<username>')
def login(username):
    if username in users:
        session['username'] = username
        return f'<h1>登录成功!</h1><p>欢迎,{users[username]["name"]}</p><a href="/">返回首页</a>'
    return '<h1>用户不存在</h1>'

# 登出路由
@app.route('/logout')
def logout():
    session.pop('username', None)
    return '<h1>已登出</h1><a href="/">返回首页</a>'

# 首页路由
@app.route('/')
def index():
    # current_user已经在模板上下文中,不需要传递
    return render_template('index.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

模板中使用用户信息:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>欢迎访问</h1>

    <!-- 检查用户是否登录 -->
    {% if current_user %}
        <p>当前用户:{{ current_user.name }}</p>
        <p>邮箱:{{ current_user.email }}</p>
        <a href="/logout">登出</a>
    {% else %}
        <p>您还未登录</p>
        <a href="/login/admin">登录</a>
    {% endif %}
</body>
</html>

注意事项:

  1. 性能考虑:context_processor函数在每次渲染模板时都会调用,避免在其中执行耗时操作
  2. 变量覆盖:如果视图函数传递了同名变量,会覆盖context_processor注入的变量
  3. 返回值:context_processor函数必须返回一个字典
  4. 命名规范:使用有意义的变量名,避免与视图函数传递的变量冲突

最佳实践:

  1. 按功能分组:将相关的变量和函数放在同一个context_processor中
  2. 使用缓存:对于不经常变化的数据,考虑使用缓存
  3. 文档说明:在代码中注释说明哪些变量是全局可用的
  4. 避免过度使用:只注入真正需要在多个模板中使用的变量

2.8 Flash Messages(闪存消息) #

Flash Messages 是 Flask 提供的在请求之间传递消息的机制。它通常用于在操作完成后(如登录成功、数据保存成功)向用户显示提示信息。

为什么需要 Flash Messages?

问题场景:

  • 用户提交表单后,需要重定向到另一个页面
  • 重定向后,原来的页面数据已经丢失
  • 需要在新的页面上显示操作结果(成功或失败)

传统方式的问题:

# 不好的做法:使用URL参数传递消息
@app.route('/login', methods=['POST'])
def login():
    if login_success:
        return redirect('/dashboard?message=登录成功')  # URL参数方式,不优雅

使用 Flash Messages 的解决方案:

# 好的做法:使用 Flash Messages
from flask import flash, redirect, url_for

@app.route('/login', methods=['POST'])
def login():
    if login_success:
        flash('登录成功!', 'success')  # 存储消息
        return redirect(url_for('dashboard'))  # 重定向后消息仍然可用

基本用法:

# 导入Flask类和flash、get_flashed_messages函数
# flash用于存储消息
# get_flashed_messages用于获取消息
from flask import Flask, flash, redirect, url_for, render_template, request, session

# 创建Flask应用实例
app = Flask(__name__)

# 设置密钥(Flash Messages需要session支持,session需要密钥)
app.config['SECRET_KEY'] = 'your-secret-key-here'

# 模拟用户数据(实际应用中来自数据库)
users = {
    'admin': '123456',
    'user1': 'password123'
}

# 定义登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 判断请求方法
    if request.method == 'POST':
        # 获取表单数据
        username = request.form.get('username', '')
        password = request.form.get('password', '')

        # 验证用户名和密码
        if username in users and users[username] == password:
            # 登录成功,存储用户信息到session
            session['username'] = username
            # 使用flash()存储成功消息
            # 第一个参数是消息内容,第二个参数是消息类别(可选)
            flash('登录成功!欢迎回来。', 'success')
            # 重定向到首页
            return redirect(url_for('index'))
        else:
            # 登录失败,存储错误消息
            flash('用户名或密码错误,请重试。', 'error')
            # 重新显示登录页面
            return redirect(url_for('login'))
    else:
        # GET请求,显示登录表单
        return render_template('login.html')

# 定义登出路由
@app.route('/logout')
def logout():
    # 清除session中的用户信息
    session.pop('username', None)
    # 存储登出成功消息
    flash('您已成功登出。', 'info')
    # 重定向到首页
    return redirect(url_for('index'))

# 定义首页路由
@app.route('/')
def index():
    # 渲染首页模板(Flash Messages会自动在模板中显示)
    return render_template('index.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

在模板中显示 Flash Messages:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <style>
        /* Flash Messages 样式 */
        .flash-messages {
            margin: 20px 0;
        }
        .flash-message {
            padding: 15px;
            margin-bottom: 10px;
            border-radius: 5px;
            border-left: 4px solid;
        }
        .flash-success {
            background-color: #d4edda;
            border-color: #28a745;
            color: #155724;
        }
        .flash-error {
            background-color: #f8d7da;
            border-color: #dc3545;
            color: #721c24;
        }
        .flash-info {
            background-color: #d1ecf1;
            border-color: #17a2b8;
            color: #0c5460;
        }
        .flash-warning {
            background-color: #fff3cd;
            border-color: #ffc107;
            color: #856404;
        }
    </style>
</head>
<body>
    <h1>欢迎访问</h1>

    <!-- 显示 Flash Messages -->
    <!-- get_flashed_messages()获取所有Flash消息 -->
    <!-- with_categories=True表示同时获取消息类别 -->
    {% with messages = get_flashed_messages(with_categories=true) %}
        {% if messages %}
            <div class="flash-messages">
                <!-- 遍历所有消息 -->
                {% for category, message in messages %}
                    <!-- 根据消息类别应用不同的样式 -->
                    <div class="flash-message flash-{{ category }}">
                        {{ message }}
                    </div>
                {% endfor %}
            </div>
        {% endif %}
    {% endwith %}

    <!-- 页面内容 -->
    {% if session.username %}
        <p>当前用户:{{ session.username }}</p>
        <a href="{{ url_for('logout') }}">登出</a>
    {% else %}
        <p>您还未登录</p>
        <a href="{{ url_for('login') }}">登录</a>
    {% endif %}
</body>
</html>

创建登录页面模板:

<!-- templates/login.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 400px;
            margin: 50px auto;
            padding: 20px;
        }
        .flash-message {
            padding: 15px;
            margin-bottom: 15px;
            border-radius: 5px;
            border-left: 4px solid;
        }
        .flash-error {
            background-color: #f8d7da;
            border-color: #dc3545;
            color: #721c24;
        }
        form {
            background-color: #f9f9f9;
            padding: 20px;
            border-radius: 5px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input {
            width: 100%;
            padding: 8px;
            margin-bottom: 15px;
            border: 1px solid #ddd;
            border-radius: 3px;
            box-sizing: border-box;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            width: 100%;
        }
    </style>
</head>
<body>
    <h1>用户登录</h1>

    <!-- 显示 Flash Messages(如果有错误消息) -->
    {% with messages = get_flashed_messages(with_categories=true) %}
        {% if messages %}
            {% for category, message in messages %}
                <div class="flash-message flash-{{ category }}">
                    {{ message }}
                </div>
            {% endfor %}
        {% endif %}
    {% endwith %}

    <!-- 登录表单 -->
    <form method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>

        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>

        <button type="submit">登录</button>
    </form>

    <p><a href="{{ url_for('index') }}">返回首页</a></p>
</body>
</html>

消息类别:

Flash Messages 支持不同的消息类别,可以根据类别显示不同的样式:

# 导入Flask类和flash函数
from flask import Flask, flash, redirect, url_for, render_template

# 创建Flask应用实例
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'

# 定义路由,演示不同类别的消息
@app.route('/test')
def test():
    # 成功消息(通常用绿色显示)
    flash('操作成功完成!', 'success')

    # 错误消息(通常用红色显示)
    flash('操作失败,请重试。', 'error')

    # 信息消息(通常用蓝色显示)
    flash('这是一条提示信息。', 'info')

    # 警告消息(通常用黄色显示)
    flash('请注意:这是一个警告。', 'warning')

    # 重定向到显示页面
    return redirect(url_for('show_messages'))

# 定义显示消息的路由
@app.route('/messages')
def show_messages():
    # 渲染模板,显示所有Flash Messages
    return render_template('messages.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

实际应用示例:用户注册和登录:

# 导入Flask类和所需函数
from flask import Flask, flash, redirect, url_for, render_template, request, session

# 创建Flask应用实例
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'

# 模拟用户数据库(实际应用中应该使用真实数据库)
users_db = {}

# 定义注册路由
@app.route('/register', methods=['GET', 'POST'])
def register():
    # 判断请求方法
    if request.method == 'POST':
        # 获取表单数据
        username = request.form.get('username', '').strip()
        email = request.form.get('email', '').strip()
        password = request.form.get('password', '')

        # 验证输入
        if not username or not email or not password:
            flash('请填写所有必填字段。', 'error')
            return redirect(url_for('register'))

        # 检查用户名是否已存在
        if username in users_db:
            flash('用户名已存在,请选择其他用户名。', 'error')
            return redirect(url_for('register'))

        # 检查邮箱是否已注册
        if any(u['email'] == email for u in users_db.values()):
            flash('该邮箱已被注册,请使用其他邮箱。', 'error')
            return redirect(url_for('register'))

        # 保存用户信息(实际应用中应该加密密码)
        users_db[username] = {
            'email': email,
            'password': password  # 实际应用中应该使用哈希加密
        }

        # 注册成功,显示成功消息
        flash('注册成功!请登录。', 'success')
        # 重定向到登录页面
        return redirect(url_for('login'))
    else:
        # GET请求,显示注册表单
        return render_template('register.html')

# 定义登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 判断请求方法
    if request.method == 'POST':
        # 获取表单数据
        username = request.form.get('username', '').strip()
        password = request.form.get('password', '')

        # 验证用户名和密码
        if username in users_db and users_db[username]['password'] == password:
            # 登录成功,存储用户信息到session
            session['username'] = username
            # 显示成功消息
            flash(f'欢迎回来,{username}!', 'success')
            # 重定向到首页
            return redirect(url_for('index'))
        else:
            # 登录失败,显示错误消息
            flash('用户名或密码错误,请重试。', 'error')
            # 重新显示登录页面
            return redirect(url_for('login'))
    else:
        # GET请求,显示登录表单
        return render_template('login.html')

# 定义登出路由
@app.route('/logout')
def logout():
    # 获取当前用户名(用于显示消息)
    username = session.get('username', '')
    # 清除session
    session.pop('username', None)
    # 显示登出消息
    if username:
        flash(f'再见,{username}!您已成功登出。', 'info')
    # 重定向到首页
    return redirect(url_for('index'))

# 定义首页路由
@app.route('/')
def index():
    # 渲染首页模板
    return render_template('index.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

创建基础模板(包含 Flash Messages 显示):

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}我的应用{% endblock %}</title>
    <style>
        /* 全局样式 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }

        /* Flash Messages 样式 */
        .flash-messages {
            margin: 20px 0;
        }
        .flash-message {
            padding: 15px 20px;
            margin-bottom: 10px;
            border-radius: 5px;
            border-left: 4px solid;
            position: relative;
            animation: slideIn 0.3s ease-out;
        }
        @keyframes slideIn {
            from {
                opacity: 0;
                transform: translateX(-20px);
            }
            to {
                opacity: 1;
                transform: translateX(0);
            }
        }
        .flash-success {
            background-color: #d4edda;
            border-color: #28a745;
            color: #155724;
        }
        .flash-error {
            background-color: #f8d7da;
            border-color: #dc3545;
            color: #721c24;
        }
        .flash-info {
            background-color: #d1ecf1;
            border-color: #17a2b8;
            color: #0c5460;
        }
        .flash-warning {
            background-color: #fff3cd;
            border-color: #ffc107;
            color: #856404;
        }

        /* 内容区域样式 */
        .content {
            background-color: white;
            padding: 30px;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
    </style>
    {% block extra_css %}{% endblock %}
</head>
<body>
    <div class="container">
        <!-- Flash Messages 显示区域 -->
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                <div class="flash-messages">
                    {% for category, message in messages %}
                        <div class="flash-message flash-{{ category }}">
                            {{ message }}
                        </div>
                    {% endfor %}
                </div>
            {% endif %}
        {% endwith %}

        <!-- 页面内容 -->
        <div class="content">
            {% block content %}{% endblock %}
        </div>
    </div>

    {% block extra_js %}{% endblock %}
</body>
</html>

注意事项:

  1. 需要设置 SECRET_KEY:Flash Messages 依赖 session,session 需要 SECRET_KEY 来加密
  2. 消息只显示一次:Flash Messages 在获取后会自动清除,不会重复显示
  3. 消息类别:建议使用标准的类别名称(success、error、info、warning),便于统一样式
  4. 重定向后显示:Flash Messages 通常在重定向后的页面显示,确保用户能看到操作结果

最佳实践:

  1. 统一消息样式:在基础模板中定义 Flash Messages 的样式,所有页面统一使用
  2. 使用消息类别:根据消息类型使用不同的类别,提供更好的用户体验
  3. 消息内容清晰:消息内容要简洁明了,让用户清楚知道发生了什么
  4. 及时反馈:在操作完成后立即显示消息,不要延迟

3. 应用配置 #

Flask应用需要各种配置,如数据库连接、密钥、调试模式等。Flask提供了灵活的配置管理方式。

3.1 为什么需要配置管理? #

问题场景:

  • 开发环境和生产环境需要不同的配置(如数据库地址、调试模式)
  • 配置信息分散在代码中,难以管理和修改
  • 敏感信息(如密钥)不应该硬编码在代码中

解决方案:

  • 将配置集中管理
  • 使用配置文件或配置类
  • 根据环境加载不同的配置

3.2 基本配置方式 #

方式1:直接设置配置

# 导入Flask类
from flask import Flask

# 创建Flask应用实例
app = Flask(__name__)

# 直接设置配置项
# app.config是一个字典,可以像普通字典一样操作
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['DATABASE_URI'] = 'sqlite:///mydatabase.db'

# 也可以使用update()方法批量设置
app.config.update(
    DEBUG=True,
    SECRET_KEY='your-secret-key-here',
    DATABASE_URI='sqlite:///mydatabase.db'
)

# 访问配置
print(app.config['DEBUG'])  # 输出: True

方式2:从环境变量读取

# 导入Flask类和os模块
from flask import Flask
import os

# 创建Flask应用实例
app = Flask(__name__)

# 从环境变量读取配置
# os.getenv()获取环境变量,如果不存在则使用默认值
app.config['DEBUG'] = os.getenv('FLASK_DEBUG', 'False') == 'True'
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'default-secret-key')
app.config['DATABASE_URI'] = os.getenv('DATABASE_URI', 'sqlite:///mydatabase.db')

3.3 使用配置类(推荐方式) #

将配置组织成类,更清晰、更易管理。

创建配置类:

# config.py
# 配置类文件,定义不同环境的配置

# 基础配置类(所有环境共用的配置)
class Config:
    # 应用密钥,用于加密会话等
    # 实际应用中应该使用环境变量或密钥管理服务
    SECRET_KEY = 'dev-secret-key-change-in-production'

    # 数据库配置
    DATABASE_URI = 'sqlite:///mydatabase.db'

    # 其他通用配置
    MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 最大上传文件大小:16MB

# 开发环境配置
class DevelopmentConfig(Config):
    # 开发环境开启调试模式
    DEBUG = True
    # 开发环境的数据库
    DATABASE_URI = 'sqlite:///dev_database.db'

# 生产环境配置
class ProductionConfig(Config):
    # 生产环境关闭调试模式
    DEBUG = False
    # 生产环境的数据库
    DATABASE_URI = 'postgresql://user:password@localhost/prod_database'

# 测试环境配置
class TestingConfig(Config):
    # 测试环境开启测试模式
    TESTING = True
    # 测试环境使用内存数据库
    DATABASE_URI = 'sqlite:///:memory:'

# 配置字典,方便根据环境名称获取配置
config = {
    'development': DevelopmentConfig,
    'production': ProductionConfig,
    'testing': TestingConfig,
    'default': DevelopmentConfig  # 默认使用开发环境配置
}

在主应用中使用配置类:

# app.py
# 导入Flask类
from flask import Flask

# 导入配置类
from config import config

# 创建Flask应用实例
app = Flask(__name__)

# 从环境变量获取配置环境名称,默认为'development'
import os
config_name = os.getenv('FLASK_ENV', 'development')

# 使用from_object()方法加载配置类
# from_object()会加载配置类中所有大写的属性作为配置项
app.config.from_object(config[config_name])

# 验证配置是否加载成功
print(f"当前环境: {config_name}")
print(f"调试模式: {app.config['DEBUG']}")
print(f"数据库URI: {app.config['DATABASE_URI']}")

# 定义路由
@app.route('/')
def index():
    return '<h1>首页</h1>'

# 运行应用
if __name__ == '__main__':
    app.run(debug=app.config['DEBUG'])

3.4 app.config.from_object() #

app.config.from_object() 是Flask提供的从对象(类或模块)加载配置的方法。

语法:

app.config.from_object(obj)

参数:

  • obj:配置对象,可以是:
    • 配置类(推荐)
    • Python模块(包含配置变量的模块)
    • 配置对象的字符串路径(如 'config.DevelopmentConfig')

工作原理:

  • from_object() 会遍历对象的所有属性
  • 只加载大写的属性作为配置项
  • 忽略私有属性(以下划线开头的属性)
  • 忽略方法和特殊属性

示例1:从配置类加载

# config.py
class Config:
    DEBUG = True
    SECRET_KEY = 'my-secret-key'
    DATABASE_URI = 'sqlite:///app.db'

    # 小写属性不会被加载
    database_name = 'mydb'

    # 私有属性不会被加载
    _private_key = 'private'

    # 方法不会被加载
    def get_database(self):
        return self.DATABASE_URI

# app.py
from flask import Flask
from config import Config

app = Flask(__name__)

# 加载配置类
app.config.from_object(Config)

# 验证配置
print(app.config['DEBUG'])        # 输出: True
print(app.config['SECRET_KEY'])    # 输出: 'my-secret-key'
print(app.config['DATABASE_URI'])  # 输出: 'sqlite:///app.db'

# 小写属性不会被加载
print('database_name' in app.config)  # 输出: False

示例2:从模块加载

# config_module.py
# 配置模块,直接定义配置变量

DEBUG = True
SECRET_KEY = 'my-secret-key'
DATABASE_URI = 'sqlite:///app.db'

# app.py
from flask import Flask
import config_module

app = Flask(__name__)

# 从模块加载配置
app.config.from_object(config_module)

# 验证配置
print(app.config['DEBUG'])        # 输出: True
print(app.config['SECRET_KEY'])    # 输出: 'my-secret-key'

示例3:使用字符串路径

# config.py
class DevelopmentConfig:
    DEBUG = True
    SECRET_KEY = 'dev-key'

# app.py
from flask import Flask

app = Flask(__name__)

# 使用字符串路径加载配置
# 格式: '模块路径.类名'
app.config.from_object('config.DevelopmentConfig')

# 验证配置
print(app.config['DEBUG'])  # 输出: True

3.5 多环境配置管理 #

在实际项目中,通常需要管理多个环境的配置。

完整的配置管理示例:

项目结构:

my_app/
│   app.py
│   config.py
│   .env              # 环境变量文件(可选)

config.py(配置类文件):

# config.py
import os

# 基础配置类
class Config:
    # 从环境变量读取,如果没有则使用默认值
    SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-me')

    # 数据库配置
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI', 'sqlite:///app.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    # 其他配置
    MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 16MB

# 开发环境配置
class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.getenv('DEV_DATABASE_URI', 'sqlite:///dev.db')

# 生产环境配置
class ProductionConfig(Config):
    DEBUG = False
    # 生产环境应该从环境变量读取敏感信息
    SECRET_KEY = os.getenv('SECRET_KEY')  # 必须设置,不能有默认值
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI')  # 必须设置

# 测试环境配置
class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'  # 内存数据库,测试后自动删除

# 配置字典
config = {
    'development': DevelopmentConfig,
    'production': ProductionConfig,
    'testing': TestingConfig,
    'default': DevelopmentConfig
}

app.py(主应用文件):

# app.py
from flask import Flask
from config import config
import os

# 创建Flask应用实例
app = Flask(__name__)

# 从环境变量获取配置环境名称
# FLASK_ENV环境变量可以在系统环境变量中设置,或在.env文件中设置
config_name = os.getenv('FLASK_ENV', 'development')

# 使用from_object()加载配置
app.config.from_object(config[config_name])

# 可选:从环境变量文件加载额外配置
# 需要安装python-dotenv: pip install python-dotenv
# from dotenv import load_dotenv
# load_dotenv()  # 从.env文件加载环境变量

# 定义路由
@app.route('/')
def index():
    return f'<h1>当前环境: {config_name}</h1><p>调试模式: {app.config["DEBUG"]}</p>'

# 运行应用
if __name__ == '__main__':
    app.run(debug=app.config['DEBUG'])

使用方式:

# 开发环境(默认)
python app.py

# 生产环境
export FLASK_ENV=production
python app.py

# 测试环境
export FLASK_ENV=testing
python app.py

3.6 配置的优先级 #

Flask配置的加载顺序(优先级从高到低):

  1. 直接设置:app.config['KEY'] = 'value'(最高优先级)
  2. from_object():app.config.from_object(Config)
  3. from_envvar():从环境变量指定的文件加载
  4. from_pyfile():从Python文件加载
  5. 默认值:Flask内置的默认配置(最低优先级)

示例:

from flask import Flask
from config import Config

app = Flask(__name__)

# 1. 先加载配置类
app.config.from_object(Config)

# 2. 然后可以覆盖特定配置
app.config['DEBUG'] = False  # 覆盖配置类中的DEBUG值

# 3. 或者从环境变量覆盖
import os
if os.getenv('FORCE_DEBUG'):
    app.config['DEBUG'] = True  # 环境变量强制开启调试模式

3.7 配置的最佳实践 #

1. 使用配置类组织配置:

  • 基础配置类包含所有环境共用的配置
  • 不同环境继承基础配置类,只覆盖需要改变的配置

2. 敏感信息使用环境变量:

  • 密钥、数据库密码等敏感信息不应该硬编码
  • 使用环境变量或密钥管理服务

3. 配置文件不要提交到版本控制:

  • 将包含敏感信息的配置文件添加到 .gitignore
  • 提供配置模板文件(如 config.example.py)

4. 使用配置字典管理多环境:

  • 通过配置字典方便切换环境
  • 使用环境变量指定当前环境

5. 验证必需配置:

# 在应用启动时验证必需配置
required_config = ['SECRET_KEY', 'DATABASE_URI']
for key in required_config:
    if not app.config.get(key):
        raise ValueError(f'必需配置 {key} 未设置')

4. 返回JSON数据(API) #

Flask不仅可以返回HTML,还可以返回JSON数据,用于构建API。

5.1 简单的API示例 #

# 导入Flask类和jsonify函数
# Flask用于创建Web应用
# jsonify用于将Python字典转换为JSON响应
from flask import Flask, jsonify

# 创建Flask应用实例
app = Flask(__name__)

# 模拟用户数据(实际应用中来自数据库)
users = [
    {'id': 1, 'name': '张三', 'email': 'zhangsan@example.com'},
    {'id': 2, 'name': '李四', 'email': 'lisi@example.com'},
    {'id': 3, 'name': '王五', 'email': 'wangwu@example.com'},
]

# 定义获取所有用户的API
# 返回JSON格式的数据
@app.route('/api/users', methods=['GET'])
def get_users():
    # 使用jsonify将Python字典列表转换为JSON响应
    # jsonify会自动设置Content-Type为application/json
    return jsonify(users)

# 定义获取单个用户的API
# <int:user_id>是URL参数,必须是整数
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    # 根据ID查找用户
    user = next((u for u in users if u['id'] == user_id), None)

    # 如果用户不存在,返回404错误
    if user is None:
        # jsonify也可以返回错误信息
        return jsonify({'error': '用户未找到'}), 404

    # 返回用户信息
    return jsonify(user)

# 定义创建用户的API
# 使用POST方法创建新资源
@app.route('/api/users', methods=['POST'])
def create_user():
    # 从请求中获取JSON数据
    # request.json包含请求体中的JSON数据
    from flask import request
    data = request.json

    # 简单的验证
    if not data or 'name' not in data or 'email' not in data:
        return jsonify({'error': '缺少必填字段'}), 400

    # 创建新用户(实际应用中应该保存到数据库)
    new_user = {
        'id': len(users) + 1,
        'name': data['name'],
        'email': data['email']
    }
    users.append(new_user)

    # 返回新创建的用户信息
    return jsonify(new_user), 201  # 201表示资源创建成功

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

5.2 测试API #

可以使用Python的requests库测试API:

# 导入requests库用于发送HTTP请求
import requests

# 定义API的基础URL
base_url = 'http://127.0.0.1:5000/api'

# 测试1:获取所有用户
print("测试1:获取所有用户")
response = requests.get(f'{base_url}/users')
# 打印响应状态码
print(f"状态码: {response.status_code}")
# 打印响应内容(JSON格式)
print(f"响应: {response.json()}")

# 测试2:获取单个用户
print("\n测试2:获取用户ID为1的用户")
response = requests.get(f'{base_url}/users/1')
print(f"状态码: {response.status_code}")
print(f"响应: {response.json()}")

# 测试3:创建新用户
print("\n测试3:创建新用户")
new_user = {
    'name': '赵六',
    'email': 'zhaoliu@example.com'
}
# 发送POST请求,传递JSON数据
response = requests.post(f'{base_url}/users', json=new_user)
print(f"状态码: {response.status_code}")
print(f"响应: {response.json()}")

6. 组织大型应用:Blueprint #

6.1 什么是Blueprint? #

当Flask应用变得越来越大时,把所有路由都写在一个文件中会变得难以维护。Blueprint(蓝图)是Flask提供的组织大型应用的方式,它可以将应用分解成多个模块,每个模块负责不同的功能。

生活中的类比:

  • 没有Blueprint:就像把所有东西都放在一个大箱子里,找东西很困难
  • 使用Blueprint:就像把东西分类放在不同的抽屉里,每个抽屉有标签,找东西很容易

Blueprint的优势:

  • 模块化:将不同功能分离到不同模块
  • 可复用:可以在多个应用中复用Blueprint
  • 易维护:代码结构清晰,易于理解和维护
  • 团队协作:不同开发者可以负责不同的Blueprint

6.2 为什么需要Blueprint? #

问题场景: 假设你有一个包含以下功能的Web应用:

  • 用户管理(注册、登录、个人资料)
  • 博客功能(文章列表、文章详情、发布文章)
  • 评论功能(发表评论、删除评论)

如果所有路由都写在一个文件中:

# 不好的做法:所有路由在一个文件中
from flask import Flask

app = Flask(__name__)

# 用户相关路由
@app.route('/user/register')
def register():
    pass

@app.route('/user/login')
def login():
    pass

# 博客相关路由
@app.route('/blog')
def blog_list():
    pass

@app.route('/blog/<id>')
def blog_detail(id):
    pass

# 评论相关路由
@app.route('/comment/add')
def add_comment():
    pass

# ... 更多路由
# 文件会变得很长,难以维护

使用Blueprint的解决方案: 将不同功能分离到不同的Blueprint中,代码更清晰、更易维护。

6.3 创建第一个Blueprint #

让我们创建一个简单的Blueprint示例:

项目结构:

my_app/
│   app.py              # 主应用文件
│
├───blueprints/         # Blueprint目录
│   ├──__init__.py      # 包初始化文件
│   └──auth.py          # 认证相关的Blueprint
│
└───templates/          # 模板目录
    └──auth/
        login.html
        register.html

创建Blueprint(blueprints/auth.py):

# 导入Blueprint类
# Blueprint用于创建可复用的路由模块
from flask import Blueprint, render_template, request, redirect, url_for

# 创建Blueprint实例
# 第一个参数'auth'是Blueprint的名称
# 第二个参数__name__用于确定Blueprint的根路径
# url_prefix='/auth'表示所有路由都会自动添加'/auth'前缀
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

# 在Blueprint中定义路由
# 实际URL会是 /auth/login(因为有url_prefix)
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    # 判断请求方法
    if request.method == 'POST':
        # 获取表单数据
        username = request.form.get('username', '')
        password = request.form.get('password', '')

        # 简单的验证(实际应用中应该连接数据库)
        if username == 'admin' and password == '123456':
            # 登录成功,重定向到首页
            # url_for('index')会生成首页的URL
            return redirect(url_for('index'))
        else:
            # 登录失败,显示错误信息
            error = '用户名或密码错误'
            return render_template('auth/login.html', error=error)
    else:
        # GET请求,显示登录表单
        return render_template('auth/login.html')

# 定义注册路由
# 实际URL会是 /auth/register
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
    # 判断请求方法
    if request.method == 'POST':
        # 获取表单数据
        username = request.form.get('username', '')
        email = request.form.get('email', '')
        password = request.form.get('password', '')

        # 简单的验证
        if not username or not email or not password:
            error = '请填写所有字段'
            return render_template('auth/register.html', error=error)

        # 在实际应用中,这里应该将用户保存到数据库
        # 这里只是简单返回成功信息
        return f'<h1>注册成功!</h1><p>用户名:{username}</p><p><a href="/auth/login">去登录</a></p>'
    else:
        # GET请求,显示注册表单
        return render_template('auth/register.html')

在主应用中注册Blueprint(app.py):

# 导入Flask类
from flask import Flask, render_template

# 导入Blueprint
# 从blueprints包中导入auth模块
from blueprints.auth import auth_bp

# 创建Flask应用实例
app = Flask(__name__)

# 注册Blueprint
# register_blueprint()方法将Blueprint注册到应用中
app.register_blueprint(auth_bp)

# 定义首页路由
@app.route('/')
def index():
    # 渲染首页模板
    return render_template('index.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

创建模板文件(templates/auth/login.html):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 400px;
            margin: 50px auto;
            padding: 20px;
        }
        form {
            background-color: #f9f9f9;
            padding: 20px;
            border-radius: 5px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input {
            width: 100%;
            padding: 8px;
            margin-bottom: 15px;
            border: 1px solid #ddd;
            border-radius: 3px;
            box-sizing: border-box;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            width: 100%;
        }
        .error {
            color: red;
            margin-bottom: 15px;
        }
    </style>
</head>
<body>
    <h1>用户登录</h1>

    <!-- 如果有错误信息,显示错误 -->
    {% if error %}
    <div class="error">{{ error }}</div>
    {% endif %}

    <!-- 登录表单 -->
    <form method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>

        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>

        <button type="submit">登录</button>
    </form>

    <p><a href="{{ url_for('auth.register') }}">还没有账号?去注册</a></p>
</body>
</html>

创建注册模板(templates/auth/register.html):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 400px;
            margin: 50px auto;
            padding: 20px;
        }
        form {
            background-color: #f9f9f9;
            padding: 20px;
            border-radius: 5px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input {
            width: 100%;
            padding: 8px;
            margin-bottom: 15px;
            border: 1px solid #ddd;
            border-radius: 3px;
            box-sizing: border-box;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            width: 100%;
        }
        .error {
            color: red;
            margin-bottom: 15px;
        }
    </style>
</head>
<body>
    <h1>用户注册</h1>

    <!-- 如果有错误信息,显示错误 -->
    {% if error %}
    <div class="error">{{ error }}</div>
    {% endif %}

    <!-- 注册表单 -->
    <form method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>

        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" required>

        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>

        <button type="submit">注册</button>
    </form>

    <p><a href="{{ url_for('auth.login') }}">已有账号?去登录</a></p>
</body>
</html>

6.4 多个Blueprint示例 #

让我们创建一个更完整的示例,包含多个Blueprint:

项目结构:

my_app/
│   app.py
│
├───blueprints/
│   ├──__init__.py
│   ├──auth.py          # 认证Blueprint
│   ├──blog.py          # 博客Blueprint
│   └──api.py           # API Blueprint
│
└───templates/
    ├──index.html
    ├──auth/
    │   ├──login.html
    │   └──register.html
    └──blog/
        ├──list.html
        └──detail.html

创建博客Blueprint(blueprints/blog.py):

# 导入Blueprint类和render_template函数
from flask import Blueprint, render_template

# 创建博客Blueprint
# url_prefix='/blog'表示所有路由都会添加'/blog'前缀
blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

# 模拟博客文章数据
# 在实际应用中,这些数据来自数据库
posts = [
    {
        'id': 1,
        'title': '第一篇文章',
        'content': '这是我的第一篇博客文章内容。',
        'author': '小明',
        'date': '2024-01-01'
    },
    {
        'id': 2,
        'title': '学习Flask',
        'content': '今天学习了Flask的Blueprint,非常有用!',
        'author': '小红',
        'date': '2024-01-02'
    }
]

# 定义博客列表路由
# 实际URL是 /blog/
@blog_bp.route('/')
def list_posts():
    # 渲染博客列表模板
    return render_template('blog/list.html', posts=posts)

# 定义文章详情路由
# 实际URL是 /blog/<post_id>
@blog_bp.route('/<int:post_id>')
def show_post(post_id):
    # 根据ID查找文章
    post = next((p for p in posts if p['id'] == post_id), None)

    # 如果文章不存在,返回404
    if post is None:
        return '<h1>文章未找到</h1>', 404

    # 渲染文章详情模板
    return render_template('blog/detail.html', post=post)

创建API Blueprint(blueprints/api.py):

# 导入Blueprint类和jsonify函数
from flask import Blueprint, jsonify

# 创建API Blueprint
# url_prefix='/api'表示所有路由都会添加'/api'前缀
api_bp = Blueprint('api', __name__, url_prefix='/api')

# 模拟用户数据
users = [
    {'id': 1, 'name': '张三', 'email': 'zhangsan@example.com'},
    {'id': 2, 'name': '李四', 'email': 'lisi@example.com'},
]

# 定义获取所有用户的API
# 实际URL是 /api/users
@api_bp.route('/users', methods=['GET'])
def get_users():
    # 返回JSON格式的用户列表
    return jsonify(users)

# 定义获取单个用户的API
# 实际URL是 /api/users/<user_id>
@api_bp.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    # 根据ID查找用户
    user = next((u for u in users if u['id'] == user_id), None)

    # 如果用户不存在,返回404
    if user is None:
        return jsonify({'error': '用户未找到'}), 404

    # 返回用户信息
    return jsonify(user)

更新主应用(app.py):

# 导入Flask类
from flask import Flask, render_template

# 导入所有Blueprint
# 从blueprints包中导入各个Blueprint模块
from blueprints.auth import auth_bp
from blueprints.blog import blog_bp
from blueprints.api import api_bp

# 创建Flask应用实例
app = Flask(__name__)

# 注册所有Blueprint
# 将不同的Blueprint注册到应用中
app.register_blueprint(auth_bp)  # 注册认证Blueprint
app.register_blueprint(blog_bp)  # 注册博客Blueprint
app.register_blueprint(api_bp)   # 注册API Blueprint

# 定义首页路由
@app.route('/')
def index():
    # 渲染首页模板
    return render_template('index.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

创建首页模板(templates/index.html):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
        }
        .nav {
            background-color: #f0f0f0;
            padding: 20px;
            border-radius: 5px;
            margin-bottom: 20px;
        }
        .nav a {
            display: inline-block;
            margin-right: 20px;
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            text-decoration: none;
            border-radius: 3px;
        }
        .nav a:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
    <h1>欢迎访问</h1>

    <div class="nav">
        <h2>导航菜单</h2>
        <!-- 使用url_for()生成Blueprint中的路由URL -->
        <!-- 'auth.login'表示auth Blueprint中的login函数 -->
        <a href="{{ url_for('auth.login') }}">登录</a>
        <a href="{{ url_for('auth.register') }}">注册</a>
        <a href="{{ url_for('blog.list_posts') }}">博客列表</a>
        <a href="{{ url_for('api.get_users') }}">API: 获取用户</a>
    </div>

    <p>这是一个使用Blueprint组织的Flask应用示例。</p>
    <p>不同的功能被分离到不同的Blueprint中,代码更清晰、更易维护。</p>
</body>
</html>

6.5 Blueprint的URL前缀和模板 #

Blueprint可以有自己的URL前缀和模板目录。

带URL前缀的Blueprint:

# 创建Blueprint时指定url_prefix
# 所有路由都会自动添加这个前缀
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

# 这个路由的实际URL是 /auth/login
@auth_bp.route('/login')
def login():
    return '登录页面'

Blueprint的模板目录:

# 创建Blueprint时指定template_folder
# Blueprint会在这个目录中查找模板
blog_bp = Blueprint('blog', __name__, 
                   url_prefix='/blog',
                   template_folder='blog_templates')

# 这个路由会从blog_templates目录中查找模板
@blog_bp.route('/')
def index():
    return render_template('index.html')  # 查找 blog_templates/index.html

完整示例:

# blueprints/blog.py
from flask import Blueprint, render_template

# 创建Blueprint,指定URL前缀和模板目录
blog_bp = Blueprint('blog', __name__, 
                   url_prefix='/blog',
                   template_folder='blog_templates')

# 定义路由
@blog_bp.route('/')
def index():
    # 会从blog_templates目录查找模板
    return render_template('index.html')

6.6 Blueprint之间的通信 #

不同的Blueprint可以通过url_for()函数相互引用。

示例:

# blueprints/auth.py
from flask import Blueprint, render_template, redirect, url_for

auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

@auth_bp.route('/login')
def login():
    # 登录成功后,重定向到博客首页
    # 'blog.index'表示blog Blueprint中的index函数
    return redirect(url_for('blog.index'))

# blueprints/blog.py
from flask import Blueprint, render_template, url_for

blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

@blog_bp.route('/')
def index():
    # 在模板中可以链接到其他Blueprint的路由
    login_url = url_for('auth.login')
    return render_template('blog/index.html', login_url=login_url)

6.7 Blueprint的最佳实践 #

1. 按功能模块组织:

my_app/
│   app.py
│
├───blueprints/
│   ├──__init__.py
│   ├──auth.py          # 认证相关
│   ├──blog.py          # 博客相关
│   ├──admin.py         # 管理后台
│   └──api.py           # API接口
│
├───templates/
│   ├──auth/
│   ├──blog/
│   └──admin/
│
└───static/
    ├──css/
    ├──js/
    └──images/

2. 每个Blueprint独立管理:

# blueprints/blog.py
from flask import Blueprint, render_template

# 创建Blueprint
blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

# 在这个Blueprint中定义所有博客相关的路由
@blog_bp.route('/')
def index():
    return render_template('blog/index.html')

@blog_bp.route('/<int:post_id>')
def show_post(post_id):
    return render_template('blog/detail.html', post_id=post_id)

# 可以定义Blueprint级别的错误处理
@blog_bp.errorhandler(404)
def not_found(error):
    return render_template('blog/404.html'), 404

3. 在主应用中统一注册:

# app.py
from flask import Flask

# 导入所有Blueprint
from blueprints.auth import auth_bp
from blueprints.blog import blog_bp
from blueprints.api import api_bp

app = Flask(__name__)

# 统一注册所有Blueprint
app.register_blueprint(auth_bp)
app.register_blueprint(blog_bp)
app.register_blueprint(api_bp)

# 主应用只保留首页等核心路由
@app.route('/')
def index():
    return '首页'

6.8 Blueprint的常见用法总结 #

1. 基本创建:

# 创建Blueprint
bp = Blueprint('name', __name__)

# 定义路由
@bp.route('/')
def index():
    return 'Hello'

# 注册Blueprint
app.register_blueprint(bp)

2. 带URL前缀:

# 所有路由都会添加'/prefix'前缀
bp = Blueprint('name', __name__, url_prefix='/prefix')

@bp.route('/hello')  # 实际URL是 /prefix/hello
def hello():
    return 'Hello'

3. 带模板目录:

# Blueprint会在指定目录查找模板
bp = Blueprint('name', __name__, template_folder='my_templates')

@bp.route('/')
def index():
    return render_template('index.html')  # 从my_templates目录查找

4. 在模板中引用Blueprint路由:

<!-- 使用url_for()生成Blueprint中的路由URL -->
<!-- 'blueprint_name.function_name'格式 -->
<a href="{{ url_for('blog.list_posts') }}">博客列表</a>

7. 静态文件 #

Web应用通常需要CSS、JavaScript、图片等静态文件。Flask可以轻松处理这些文件。

7.1 项目结构 #

my_app/
│   app.py
│
├───static/          # 静态文件目录
│   ├───css/
│   │   style.css
│   ├───js/
│   │   script.js
│   └───images/
│       logo.png
│
└───templates/       # 模板目录
    index.html

7.2 使用静态文件 #

创建 static/css/style.css:

/* 全局样式 */
body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
    background-color: #f5f5f5;
}

/* 容器样式 */
.container {
    max-width: 800px;
    margin: 0 auto;
    background-color: white;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

/* 标题样式 */
h1 {
    color: #4CAF50;
}

/* 按钮样式 */
.btn {
    background-color: #4CAF50;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 3px;
    cursor: pointer;
}

.btn:hover {
    background-color: #45a049;
}

创建 templates/index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>我的应用</title>
    <!-- 使用url_for()函数引用CSS文件 -->
    <!-- 'static'是Flask内置的端点,filename是文件路径 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <div class="container">
        <h1>欢迎访问</h1>
        <p>这是一个使用Flask和CSS的示例页面。</p>
        <button class="btn">点击我</button>
    </div>

    <!-- 引用JavaScript文件 -->
    <script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>

创建 static/js/script.js:

// 等待页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
    // 获取按钮元素
    const btn = document.querySelector('.btn');

    // 为按钮添加点击事件
    btn.addEventListener('click', function() {
        // 显示提示框
        alert('按钮被点击了!');
    });
});

更新 app.py:

# 导入Flask类和render_template函数
from flask import Flask, render_template

# 创建Flask应用实例
app = Flask(__name__)

# 定义首页路由
@app.route('/')
def index():
    # 渲染模板(会自动加载CSS和JS)
    return render_template('index.html')

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

8. 常见问题和注意事项 #

8.1 调试模式 #

开发时建议开启调试模式,但生产环境必须关闭。

# 开发环境:开启调试模式
if __name__ == '__main__':
    app.run(debug=True)  # 开发时使用

# 生产环境:关闭调试模式
if __name__ == '__main__':
    app.run(debug=False)  # 生产环境必须关闭

8.2 端口被占用 #

如果5000端口被占用,可以修改端口:

# 修改端口为8080
if __name__ == '__main__':
    app.run(port=8080, debug=True)

8.3 模板文件找不到 #

确保模板文件在正确的目录中:

# Flask默认在templates目录中查找模板
# 如果模板在其他目录,需要指定
app = Flask(__name__, template_folder='my_templates')

8.4 静态文件找不到 #

确保静态文件在正确的目录中:

# Flask默认在static目录中查找静态文件
# 如果静态文件在其他目录,需要指定
app = Flask(__name__, static_folder='my_static')
← 上一节 fastapi 下一节 Generic →

访问验证

请输入访问令牌

Token不正确,请重新输入