flask教程


flask教程

项目结构

中型

my_flask_project/
├── app/                     # 核心应用文件夹
│   ├── __init__.py          # 应用初始化
│   ├── routes.py            # 路由定义
│   ├── models.py            # 数据模型(如需要)
│   ├── templates/           # HTML 模板
│   │   └── index.html
│   └── static/              # 静态文件
│       ├── css/
│       │   └── style.css
│       └── js/
│           └── script.js
├── config.py                # 配置文件
├── run.py                   # 启动文件
└── requirements.txt         # 依赖文件

app/__init__.py

__init__.py 中创建并初始化 Flask 应用,加载配置,并从 routes.py 注册路由。

from flask import Flask

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.Config')  # 加载配置

    with app.app_context():
        # 导入并注册路由
        from . import routes
        return app
  • 说明create_app 函数用于创建和配置应用实例。app.app_context() 确保在应用上下文中导入路由。

app/routes.py

定义项目的路由,所有路由相关的逻辑放在这个文件中。

from flask import render_template, current_app as app

@app.route('/')
def home():
    return render_template('index.html')
  • 说明app.route('/') 是根路径的路由,返回 index.html 模板页面。

app/models.py (可选)

如果项目需要数据库模型,可以在此文件中定义。例如:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(150), nullable=False, unique=True)

config.py

配置文件,用于管理全局配置(例如密钥、数据库 URI 等)。将敏感信息存储在环境变量中更安全。

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'your_secret_key'
    SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

app/templates/index.html

HTML 模板文件示例。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My Flask App</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <h1>Welcome to My Flask App</h1>
    <p>Hello, Flask with modular structure!</p>
    <script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>

app/static/css/style.css

简单的 CSS 文件,用于为页面增加样式。

/* static/css/style.css */
body {
    font-family: Arial, sans-serif;
    background-color: #f0f0f0;
}

app/static/js/script.js

简单的 JavaScript 文件。

// static/js/script.js
console.log("Welcome to My Flask App with modular structure!");

run.py

项目的启动文件,通过调用 create_app 函数启动应用。

from app import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=True)
  • 说明run.py 是整个项目的入口文件,使用 app.run(debug=True) 启动应用程序。

requirements.txt

包含项目依赖。

大型

my_flask_project/
├── app/
│   ├── __init__.py           # 初始化应用程序和配置
│   ├── routes.py             # 路由定义
│   ├── models.py             # 数据库模型定义
│   ├── templates/            # HTML 模板文件
│   │   └── base.html
│   ├── static/               # 静态文件(CSS、JS、图片等)
│   │   ├── css/
│   │   │   └── style.css
│   │   ├── js/
│   │   └── images/
│   └── blueprints/           # 各个功能模块的蓝图
│       ├── __init__.py
│       ├── user/             # 用户模块
│       │   ├── __init__.py
│       │   ├── routes.py
│       │   └── templates/
│       └── admin/            # 管理员模块
│           ├── __init__.py
│           ├── routes.py
│           └── templates/
├── migrations/               # 数据库迁移文件(使用 Flask-Migrate)
├── tests/                    # 测试代码
│   ├── __init__.py
│   └── test_app.py
├── config.py                 # 配置文件
├── manage.py                 # 启动、管理和迁移命令
└── requirements.txt          # 依赖包
  • **app/**:主要应用程序文件夹,包含所有核心代码。

    • **__init__.py**:初始化 Flask 应用、加载配置和注册蓝图。
    • **routes.py**:定义全局路由。
    • **models.py**:定义数据库模型。
    • **templates/**:存储 HTML 模板。
    • **static/**:存储静态文件,如 CSS、JavaScript 和图片。
    • **blueprints/**:存储不同功能模块的蓝图,每个模块可以有自己的 routes.pytemplates/ 文件夹。

    **migrations/**:数据库迁移文件(如使用 Flask-Migrate 时生成)。

    **tests/**:测试文件夹,用于单元测试或集成测试。

    **config.py**:存储配置文件,如数据库连接和应用的全局设置。

    **manage.py**:用于管理项目的文件,可添加自定义命令,如启动服务器、初始化数据库等。

    **requirements.txt**:记录项目的所有依赖包。

路由

1. 基本路由

最简单的路由是将一个 URL 映射到一个视图函数。可以使用 @app.route() 装饰器来定义路由。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Welcome to the homepage!"

@app.route('/about')
def about():
    return "This is the about page."
  • @app.route('/'):当用户访问根路径(如 http://localhost:5000/)时,执行 home 函数。
  • @app.route('/about'):访问 /about 时,执行 about 函数。

2. 动态路由

可以在路由中添加动态部分,用来接收 URL 中的参数。动态部分用尖括号 <...> 表示,Flask 会自动将 URL 中的值传递给视图函数。

@app.route('/user/<username>')
def show_user(username):
    return f"Hello, {username}!"
  • 访问 /user/john 时,会输出 Hello, john!
  • <username> 是一个动态参数,视图函数 show_user 将接收 username 的值。

指定数据类型

默认情况下,Flask 会将 URL 参数作为字符串处理。如果需要其他类型的数据,可以在尖括号内指定:

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f"Post ID is {post_id}"
  • <int:post_id> 指定了 post_id 必须是整数,访问 /post/5 会返回 Post ID is 5
  • 常用的数据类型有:
    • <int:variable>:整数
    • <float:variable>:浮点数
    • <path:variable>:字符串(允许包含 /

3. 多种请求方法

默认情况下,Flask 路由只响应 GET 请求。如果需要处理其他 HTTP 方法(如 POSTPUTDELETE 等),可以通过 methods 参数指定。

@app.route('/submit', methods=['GET', 'POST'])
def submit():
    if request.method == 'POST':
        return "Form submitted!"
    return "Please submit the form."
  • methods=['GET', 'POST']:允许 submit 路由响应 GETPOST 请求。
  • 使用 request.method 判断请求类型并做不同的处理。

4. 路由别名(URL 别名)

可以使用 url_for 函数生成路由的 URL,避免硬编码 URL。在视图函数上使用 endpoint 参数,可以给路由一个别名,用于简化 url_for 函数调用。

from flask import url_for

@app.route('/profile/<username>', endpoint='user_profile')
def profile(username):
    return f"User: {username}"

# 使用 url_for 动态生成 URL
@app.route('/redirect_to_profile')
def redirect_to_profile():
    return redirect(url_for('user_profile', username='john'))
  • 访问 /redirect_to_profile 会重定向到 /profile/john

5. 重定向与错误处理

可以通过 redirect 实现重定向,或用 abort 函数处理错误。

from flask import redirect, abort

@app.route('/old-url')
def old_url():
    # 重定向到新的 URL
    return redirect(url_for('new_url'))

@app.route('/new-url')
def new_url():
    return "This is the new URL."

@app.route('/secret')
def secret():
    abort(403)  # 返回 403 错误
  • redirect(url_for('new_url')):将用户重定向到 new_url 视图。
  • abort(403):返回 403 错误页面。

6. 带查询参数的路由

查询参数一般附加在 URL 的末尾,例如 http://localhost:5000/search?query=flask。可以通过 request.args 获取这些参数。

from flask import request

@app.route('/search')
def search():
    query = request.args.get('query')
    return f"Search results for: {query}"
  • request.args.get('query'):获取查询参数 query 的值。

7. 路由分组(蓝图)

如果应用较大,可以将路由划分为不同的模块(称为“蓝图”),以提高代码组织性。以下是一个简单的蓝图示例。

创建蓝图文件 app/user.py

from flask import Blueprint

user_bp = Blueprint('user', __name__)

@user_bp.route('/<username>')
def profile(username):
    return f"User Profile for {username}"

注册蓝图到应用中 app/__init__.py

from flask import Flask
from .user import user_bp

def create_app():
    app = Flask(__name__)
    app.register_blueprint(user_bp, url_prefix='/user')  # 注册蓝图

    return app
  • url_prefix='/user':访问 /user/<username> 时,会调用 user_bp 蓝图中的 profile 路由。
  • 蓝图让大型应用代码更清晰。

8. 格式化

from flask import jsonify

@app.route('/json')
def json_data():
    data = {"name": "Flask", "version": "2.0"}
    return jsonify(data)

9.视图函数的装饰器

除了 @app.route,Flask 还支持其他装饰器,用于实现更复杂的功能。

  • **@app.before_request**:在每个请求处理之前运行的函数。
  • **@app.after_request**:在每个请求处理之后运行的函数。
  • **@app.teardown_request**:在请求结束后运行的函数,用于清理工作。

示例装饰器使用:

@app.before_request
def before_request():
  print('Before request')

@app.after_request
def after_request(response):
  print('After request')
  return response

@app.teardown_request
def teardown_request(exception):
  print('Teardown request')

完整示例

以下是一个包含多种路由定义方式的完整示例:

from flask import Flask, request, redirect, url_for, abort, jsonify

app = Flask(__name__)

@app.route('/')
def home():
    return "Welcome to the homepage!"

@app.route('/user/<username>')
def show_user(username):
    return f"Hello, {username}!"

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f"Post ID is {post_id}"

@app.route('/submit', methods=['GET', 'POST'])
def submit():
    if request.method == 'POST':
        return "Form submitted!"
    return "Please submit the form."

@app.route('/search')
def search():
    query = request.args.get('query')
    return f"Search results for: {query}"

@app.route('/json')
def json_data():
    data = {"name": "Flask", "version": "2.0"}
    return jsonify(data)

@app.route('/old-url')
def old_url():
    return redirect(url_for('home'))

@app.route('/secret')
def secret():
    abort(403)

Flask 模板渲染

在 Flask 中,模板渲染是生成动态 HTML 页面的一种方法。Flask 使用 Jinja2 模板引擎来帮助你在 HTML 中插入变量、控制结构(如条件判断、循环)等内容,以便动态生成页面。

1. 设置模板文件夹

在 Flask 项目中,默认的模板文件夹是 templates,建议将所有 HTML 模板文件放在这个文件夹中。Flask 会自动寻找并渲染该目录中的模板文件。

2. 创建基本 HTML 模板

假设我们在 templates/index.html 中创建了一个基本的 HTML 模板文件:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ heading }}</h1>
    <p>{{ content }}</p>
</body>
</html>

在这个模板中:

  • {{ title }}{{ heading }}{{ content }} 是变量占位符,在渲染模板时会被 Flask 替换成实际的值。

3. 渲染模板

在 Flask 中,可以使用 render_template 函数来渲染 HTML 模板,并将变量传递给模板。下面是一个简单的视图函数,渲染上面的 index.html 模板。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html', title="Home Page", heading="Welcome to My Website", content="This is the home page content.")
  • render_template('index.html', ...)render_template 函数会在 templates 文件夹中找到 index.html 文件,并将指定变量传入模板。
  • title="Home Page":将 title 变量的值传入模板,模板中的 {{ title }} 将被替换为 Home Page

4. 模板变量和控制结构

Jinja2 模板支持在 HTML 中使用变量、控制结构(条件、循环)等操作。

变量

使用双大括号 {{ ... }} 在模板中插入变量。

<p>Hello, {{ name }}!</p>

条件语句

使用 {% if ... %}` 和 `{% endif %} 添加条件逻辑:

{% if user_is_logged_in %}
    <p>Welcome back, {{ username }}!</p>
{% else %}
    <p>Please log in to access your account.</p>
{% endif %}

循环

使用 {% for ... in ... %}` 和 `{% endfor %} 添加循环:

<ul>
    {% for item in items %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

5. 继承与块

Jinja2 支持模板继承,可以创建一个基础模板供其他模板继承。可以将公共结构(如导航栏、页脚等)放在基础模板中,然后在子模板中覆盖特定部分。

基础模板 templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
    <header>
        <h1>My Website</h1>
    </header>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>© 2024 My Website</p>
    </footer>
</body>
</html>
  • {% block title %}` 和 `{% block content %}` 是块占位符,子模板可以覆盖这些内容。 #### 子模板 `templates/index.html`
    {% extends "base.html" %}
    
    {% block title %}Home Page{% endblock %}
    
    {% block content %}
        <h2>Welcome to the Home Page</h2>
        <p>This is some content specific to the home page.</p>
    {% endblock %}
    - `{% extends "base.html" %}` 表示继承 `base.html` 模板。 - `{% block title %}Home Page{% endblock %}
    :覆盖了基础模板中的 title 块。
  • {% block content %}...{% endblock %}:覆盖了基础模板中的 content 块。

6. 使用静态文件

Flask 默认将静态文件放在 static 文件夹中,例如 CSS、JavaScript 文件等。在模板中,可以通过 url_for('static', filename='...') 生成静态文件的 URL。

假设有一个 CSS 文件 static/style.css,可以在模板中引入:

<!-- 在 base.html 中引入 CSS 文件 -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

7. 完整示例

以下是包含基础模板、变量、循环、条件的完整 Flask 应用示例。

项目结构

my_flask_app/
├── app.py
├── templates/
│   ├── base.html
│   ├── index.html
└── static/
    └── style.css

app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    items = ["Item 1", "Item 2", "Item 3"]
    user_is_logged_in = True
    return render_template('index.html', title="Home Page", items=items, user_is_logged_in=user_is_logged_in, username="John Doe")

templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Website{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <header>
        <h1>My Website</h1>
    </header>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>© 2024 My Website</p>
    </footer>
</body>
</html>

templates/index.html

{% extends "base.html" %}

{% block title %}Home Page{% endblock %}

{% block content %}
    <h2>Welcome to the Home Page</h2>
    {% if user_is_logged_in %}
        <p>Welcome back, {{ username }}!</p>
    {% else %}
        <p>Please log in to access your account.</p>
    {% endif %}

    <ul>
        {% for item in items %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
{% endblock %}

static/style.css

/* 示例样式 */
body {
    font-family: Arial, sans-serif;
}

header {
    background-color: #333;
    color: #fff;
    padding: 1em;
    text-align: center;
}

footer {
    background-color: #333;
    color: #fff;
    padding: 0.5em;
    text-align: center;
}

Flask 表单处理

在 Flask 中,表单处理是一个常见的功能,它涉及接收用户输入、验证数据以及相应地进行处理。Flask 提供了 Flask-WTF 扩展,使得表单的创建、验证和处理变得更加简单和强大。下面将详细介绍如何在 Flask 中处理表单,包括安装 Flask-WTF、创建表单、验证表单、显示表单错误和处理表单数据。

1. 安装 Flask-WTF

在开始之前,你需要确保安装了 Flask-WTF。可以通过 pip 进行安装:

pip install Flask-WTF

2. 创建 Flask 应用

创建一个简单的 Flask 应用并设置基本配置。

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'  # 设置安全密钥

3. 创建表单类

使用 Flask-WTF 创建表单类,定义所需的字段和验证规则。

class MyForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    submit = SubmitField('Submit')
  • StringField:表示一个文本输入字段。
  • DataRequired():确保字段不为空。

4. 渲染表单

在视图函数中实例化表单并传递给模板。

@app.route('/', methods=['GET', 'POST'])
def home():
    form = MyForm()
    if form.validate_on_submit():  # 验证表单数据
        username = form.username.data  # 获取输入的用户名
        return f'Hello, {username}!'
    return render_template('index.html', form=form)
  • validate_on_submit():检查请求方法是否为 POST 并验证表单数据。
  • form.username.data:访问用户输入的值。

5. 创建模板

templates 文件夹中创建一个模板 index.html,用于显示表单。

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask Form Example</title>
</head>
<body>
    <h1>Enter your username</h1>
    <form method="POST">
        {{ form.hidden_tag() }}  <!-- 防止 CSRF 攻击 -->
        {{ form.username.label }} {{ form.username(size=20) }}<br>
        {% for error in form.username.errors %}
            <span style="color: red;">[{{ error }}]</span><br>
        {% endfor %}
        {{ form.submit() }}
    </form>
</body>
</html>
  • {{ form.hidden_tag() }}:生成隐藏的 CSRF 令牌,增强安全性。
  • 使用 {% for error in form.username.errors %} 循环显示字段的验证错误。

6. 处理表单错误

如果表单验证失败,Flask-WTF 会自动将错误信息存储在字段的 errors 属性中。在模板中,可以通过条件判断显示相应的错误消息。

7. 完整示例

以下是一个完整的 Flask 应用示例,展示了如何使用 Flask-WTF 处理表单。

项目结构

my_flask_app/
├── app.py                   # Flask 应用主文件
└── templates/
    └── index.html          # 表单模板

app.py

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'  # 设置安全密钥

class MyForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    submit = SubmitField('Submit')

@app.route('/', methods=['GET', 'POST'])
def home():
    form = MyForm()
    if form.validate_on_submit():  # 验证表单数据
        username = form.username.data  # 获取输入的用户名
        return f'Hello, {username}!'
    return render_template('index.html', form=form)

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

templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask Form Example</title>
</head>
<body>
    <h1>Enter your username</h1>
    <form method="POST">
        {{ form.hidden_tag() }}  <!-- 防止 CSRF 攻击 -->
        {{ form.username.label }} {{ form.username(size=20) }}<br>
        {% for error in form.username.errors %}
            <span style="color: red;">[{{ error }}]</span><br>
        {% endfor %}
        {{ form.submit() }}
    </form>
</body>
</html>

8. 添加更多字段和验证器

可以使用 WTForms 提供的多种字段类型和验证器来扩展表单功能。例如,可以添加 EmailFieldPasswordFieldSelectField 等。

from wtforms import EmailField, PasswordField, SelectField

class ExtendedForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    email = EmailField('Email', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    options = SelectField('Options', choices=[('opt1', 'Option 1'), ('opt2', 'Option 2')])
    submit = SubmitField('Submit')

9. 处理文件上传

Flask-WTF 也支持文件上传,可以使用 FileField

from wtforms import FileField

class UploadForm(FlaskForm):
    file = FileField('File')
    submit = SubmitField('Upload')

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    form = UploadForm()
    if form.validate_on_submit():
        file = form.file.data
        # 处理文件上传
        return f'File {file.filename} uploaded successfully!'
    return render_template('upload.html', form=form)

10. 保护表单安全

使用 SECRET_KEY 保护表单,防止 CSRF 攻击,确保所有表单都使用 form.hidden_tag() 生成 CSRF 令牌。


文章作者: 0xdadream
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 0xdadream !
评论
  目录