如何在Flask应用程序中使用Flask-SQLAlchemy与数据库交互

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。

介绍

在 Web 应用程序中,您通常需要一个数据库,它是一个有组织的数据集合。 您使用数据库来存储和维护可以有效检索和操作的持久数据。 例如,在社交媒体应用程序中,您有一个数据库,其中用户数据(个人信息、帖子、评论、关注者)以一种可以有效操作的方式存储。 您可以根据不同的要求和条件,将数据添加到数据库、检索、修改或删除数据库。 在 Web 应用程序中,这些要求可能是用户添加新帖子、删除帖子或删除他们的帐户,这可能会或可能不会删除他们的帖子。 您为操作数据而执行的操作将取决于应用程序中的特定功能。 例如,您可能不希望用户添加没有标题的帖子。

Flask 是一个轻量级的 Python Web 框架,它提供了有用的工具和功能,用于用 Python 语言创建 Web 应用程序。 SQLAlchemy 是一个 SQL 工具包,为关系数据库提供高效和高性能的数据库访问。 它提供了与 SQLite、MySQL 和 PostgreSQL 等多个数据库引擎交互的方法。 它使您可以访问数据库的 SQL 功能。 它还为您提供了一个对象关系映射器 (ORM),它允许您使用简单的 Python 对象和方法进行查询和处理数据。 Flask-SQLAlchemy 是一个 Flask 扩展,它可以更轻松地将 SQLAlchemy 与 Flask 一起使用,为您提供通过 SQLAlchemy 在 Flask 应用程序中与数据库进行交互的工具和方法。

在本教程中,您将构建一个小型学生管理系统,演示如何使用 Flask-SQLAlchemy 扩展。 您将在 Flask 中使用它来执行基本任务,例如连接到数据库服务器、创建表、向表中添加数据、检索表以及更新和删除数据库中的项目。 您可以将 SQLAlchemy 与 SQLite 一起使用,尽管您也可以将它与其他数据库引擎一起使用,例如 PostgreSQL 和 MySQL。 SQLite 与 Python 配合得很好,因为 Python 标准库提供了 sqlite3 模块,SQLAlchemy 在后台使用该模块与 SQLite 数据库进行交互,而无需安装任何东西。 SQlite 默认安装在 Linux 系统上,并作为 Python 包的一部分安装在 Windows 上。

先决条件

第 1 步 — 安装 Flask 和 Flask-SQLAlchemy

在此步骤中,您将为您的应用程序安装必要的包。

激活虚拟环境后,使用 pip 安装 Flask 和 Flask-SQLAlchemy:

pip install Flask Flask-SQLAlchemy

安装成功完成后,您将在输出末尾看到类似于以下内容的行:

OutputSuccessfully installed Flask-2.0.3 Flask-SQLAlchemy-2.5.1 Jinja2-3.0.3 MarkupSafe-2.1.0 SQLAlchemy-1.4.31 Werkzeug-2.0.3 click-8.0.4 greenlet-1.1.2 itsdangerous-2.1.0

安装所需的 Python 包后,接下来您将设置数据库。

第 2 步 — 设置数据库和模型

在此步骤中,您将设置数据库连接,并创建一个 SQLAlchemy 数据库模型,它是一个 Python 类,表示存储数据的表。 您将启动数据库,根据您将声明的模型为学生创建一个表,并将一些学生添加到您的学生表中。

设置数据库连接

flask_app 目录中打开一个名为 app.py 的文件。 该文件将包含用于设置数据库和 Flask 路由的代码:

nano app.py

这个文件将连接到一个名为 database.db 的 SQLite 数据库,并有一个名为 Student 的类,它代表你的数据库学生表,用于存储学生信息,以及你的 Flask 路由。 在 app.py 的顶部添加以下 import 语句:

烧瓶应用程序/app.py

import os
from flask import Flask, render_template, request, url_for, redirect
from flask_sqlalchemy import SQLAlchemy

from sqlalchemy.sql import func

在这里,您导入 os 模块,它使您可以访问各种操作系统接口。 您将使用它为 database.db 数据库文件构建文件路径。

然后从 flask 包中导入应用程序所需的必要帮助程序:创建 Flask 应用程序实例的 Flask 类,渲染模板的 render_template() 函数, request 对象用于处理请求,url_for() 函数用于构造路由的 URL,redirect() 函数用于重定向用户。 有关路由和模板的更多信息,请参阅 如何在 Flask 应用程序中使用模板

然后,您从 Flask-SQLAlchemy 扩展中导入 SQLAlchemy 类,这使您可以访问 SQLAlchemy 中的所有函数和类,以及帮助程序以及将 Flask 与 SQLAlchemy 集成的功能。 您将使用它来创建连接到 Flask 应用程序的数据库对象,允许您使用 Python 类、对象和函数创建和操作表,而无需使用 SQL 语言。

您还可以从 sqlalchemy.sql 模块导入 func 帮助程序以访问 SQL 函数。 您需要在学生管理系统中设置默认创建日期和时间,以便创建学生记录。

在导入下方,您将设置一个数据库文件路径,实例化您的 Flask 应用程序,并使用 SQLAlchemy 配置和连接您的应用程序。 添加以下代码:

烧瓶应用程序/app.py

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
        'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

在这里,您构建 SQLite 数据库文件的路径。 您首先将基本目录定义为当前目录。 您使用 os.path.abspath() 函数来获取当前文件目录的绝对路径。 特殊的 __file__ 变量保存当前 app.py 文件的路径名。 您将基本目录的绝对路径存储在名为 basedir 的变量中。

然后创建一个名为 app 的 Flask 应用程序实例,用于配置两个 Flask-SQLAlchemy 配置键

  • SQLALCHEMY_DATABASE_URI:指定要与之建立连接的数据库的数据库 URI。 在这种情况下,URI 遵循 sqlite:///path/to/database.db 格式。 您使用 os.path.join() 函数智能地连接您构建并存储在 basedir 变量中的基本目录和 database.db 文件名。 这将连接到 flask_app 目录中的 database.db 数据库文件。 启动数据库后,将创建该文件。
  • SQLALCHEMY_TRACK_MODIFICATIONS:启用或禁用跟踪对象修改的配置。 您将其设置为 False 以禁用跟踪并使用更少的内存。 有关更多信息,请参阅 Flask-SQLAlchemy 文档中的 配置页面

笔记:

如果要使用其他数据库引擎,例如 PostgreSQL 或 MySQL,则需要使用正确的 URI。

对于 PostgreSQL,使用以下格式:

postgresql://username:password@host:port/database_name

对于 MySQL:

mysql://username:password@host:port/database_name

有关更多信息,请参阅引擎配置 SQLAlchemy 文档。


通过设置数据库 URI 并禁用跟踪来配置 SQLAlchemy 后,您使用 SQLAlchemy 类创建数据库对象,传递应用程序实例以将 Flask 应用程序与 SQLAlchemy 连接。 您将数据库对象存储在一个名为 db 的变量中。 您将使用此 db 对象与您的数据库进行交互。

声明表

建立数据库连接并创建数据库对象后,您将使用数据库对象为学生创建一个数据库表,该表由 model 表示——一个从基类 Flask 继承的 Python 类—— SQLAlchemy 通过您之前创建的 db 数据库实例提供。 要将学生表定义为模型,请将以下类添加到您的 app.py 文件中:

烧瓶应用程序/app.py

# ...

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    firstname = db.Column(db.String(100), nullable=False)
    lastname = db.Column(db.String(100), nullable=False)
    email = db.Column(db.String(80), unique=True, nullable=False)
    age = db.Column(db.Integer)
    created_at = db.Column(db.DateTime(timezone=True),
                           server_default=func.now())
    bio = db.Column(db.Text)

    def __repr__(self):
        return f'<Student {self.firstname}>'

在这里,您创建一个 Student 模型,它继承自 db.Model 类。 这代表学生表。 您使用 db.Column 类为您的表定义列。 第一个参数表示列类型,其他参数表示列配置。

您为 Student 模型定义以下列:

  • id:学生证。 您可以使用 db.Integer 将其定义为整数。 primary_key=True 将此列定义为 主键 ,它将由数据库为每个条目(即学生)分配一个唯一值。
  • firstname:学生的名字。 最大长度为 100 个字符的字符串。 nullable=False 表示此列不应为空。
  • lastname:学生的姓氏。 最大长度为 100 个字符的字符串。 nullable=False 表示此列不应为空。
  • email:学生的邮箱。 最大长度为 80 个字符的字符串。 unique=True 表示每个学生的每封电子邮件都应该是唯一的。 nullable=False 表示此列不应为空。
  • age:学生的年龄。
  • created_at:在数据库中创建学生记录的时间。 您使用 db.DateTime 将其定义为 Python datetime 对象。 timezone=True 启用时区支持。 server_default 在创建表时设置数据库中的默认值,以便默认值由数据库而不是模型处理。 您将调用 SQL now() 日期时间函数的 func.now() 函数传递给它。 在 SQLite 中,它在创建学生表时呈现为 CURRENT_TIMESTAMP
  • bio:学生的简历。 db.Text() 表示该列包含长文本。

请参阅 SQLAlchemy 文档,了解与您在前面代码块中使用的类型不同的列类型。

特殊的 __repr__ 函数允许您为每个对象提供一个字符串表示,以便在调试时识别它。 在这种情况下,您使用学生的名字。

app.py 文件现在如下所示:

烧瓶应用程序/app.py

import os
from flask import Flask, render_template, request, url_for, redirect
from flask_sqlalchemy import SQLAlchemy

from sqlalchemy.sql import func


basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
        'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)


class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    firstname = db.Column(db.String(100), nullable=False)
    lastname = db.Column(db.String(100), nullable=False)
    email = db.Column(db.String(80), unique=True, nullable=False)
    age = db.Column(db.Integer)
    created_at = db.Column(db.DateTime(timezone=True),
                           server_default=func.now())
    bio = db.Column(db.Text)

    def __repr__(self):
        return f'<Student {self.firstname}>'

保存并关闭 app.py

创建数据库

现在您已经设置了数据库连接和学生模型,您将使用 Flask shell 基于 Student 模型创建数据库和学生表。

激活虚拟环境后,使用 FLASK_APP 环境变量将 app.py 文件设置为 Flask 应用程序。 然后在 flask_app 目录中使用以下命令打开 Flask shell:

export FLASK_APP=app
flask shell

将打开一个 Python 交互式 shell。 这个特殊的 shell 在您的 Flask 应用程序的上下文中运行命令,以便您将调用的 Flask-SQLAlchemy 函数连接到您的应用程序。

导入数据库对象和学生模型,然后运行 db.create_all() 函数来创建与您的模型关联的表。 在这种情况下,您只有一个模型,这意味着函数调用只会在您的数据库中创建一个表:

from app import db, Student
db.create_all()

让 shell 继续运行,打开另一个终端窗口并导航到您的 flask_app 目录。 您现在将在 flask_app 中看到一个名为 database.db 的新文件。

笔记:

db.create_all() 函数不会重新创建或更新已存在的表。 例如,如果您通过添加新列来修改模型,并运行 db.create_all() 函数,如果表已存在于数据库中,则您对模型所做的更改将不会应用于表。 解决方案是使用 db.drop_all() 函数删除所有现有的数据库表,然后使用 db.create_all() 函数重新创建它们,如下所示:

db.drop_all()
db.create_all()

这将应用您对模型所做的修改,但也会删除数据库中的所有现有数据。 要更新数据库并保留现有数据,您需要使用 schema migration,它允许您修改表并保留数据。 您可以使用 Flask-Migrate 扩展通过 Flask 命令行界面执行 SQLAlchemy 模式迁移。


如果您收到错误,请确保您的数据库 URI 和模型声明正确。

填充表格

创建数据库和学生表后,您将使用烧瓶外壳通过 Student 模型将一些学生添加到您的数据库中。

使用您之前打开的相同的烧瓶外壳,或者在您的 flask_app 目录中激活您的虚拟环境打开一个新的:

flask shell

要将学生添加到数据库,您将导入数据库对象和 Student 模型,并创建 Student 模型的实例,通过关键字参数将学生数据传递给它,如下所示:

from app import db, Student
student_john = Student(firstname='john', lastname='doe',
                       email='jd@example.com', age=23,
                       bio='Biology student')

student_john 对象表示将被添加到数据库中的学生,但该对象尚未写入数据库。 查看烧瓶外壳中的对象以查看您使用 __repr__() 方法构造的表示字符串:

student_john

您将收到以下输出:

Output<Student john>

您可以使用在 Student 模型中定义的类属性来获取列的值:

student_john.firstname
student_john.bio
Output'john'
'Biology student'

因为这个学生还没有被添加到数据库中,所以它的 ID 是 None

print(student_john.id)
OutputNone

要将这个学生添加到数据库,您首先需要将其添加到管理数据库事务的 数据库会话。 Flask-SQLAlchemy 提供了 db.session 对象,您可以通过它管理数据库更改。 使用 db.session.add() 方法将 student_john 对象添加到会话中,以准备将其写入数据库:

db.session.add(student_john)

这将发出 INSERT 语句,但您不会得到 ID,因为数据库事务仍未提交。 要提交事务并将更改应用到数据库,请使用 db.session.commit() 方法:

db.session.commit()

现在学生 John 已添加到数据库中,您可以获取其 ID:

print(student_john.id)
Output1

您还可以使用 db.session.add() 方法编辑数据库中的项目。 例如,您可以像这样修改学生的电子邮件:

student_john.email = 'john_doe@example.com'
db.session.add(student_john)
db.session.commit()

使用 Flask shell 将更多学生添加到您的数据库中:

sammy = Student(firstname='Sammy',
               lastname='Shark',
               email='sammyshark@example.com',
               age=20,
               bio='Marine biology student')

carl = Student(firstname='Carl',
               lastname='White',
               email='carlwhite@example.com',
               age=22,
               bio='Marine geology student')

db.session.add(sammy)
db.session.add(carl)
db.session.commit()

现在,您可以使用 query 属性和 all() 方法查询学生表中的所有记录:

Student.query.all()

您将收到以下输出:

Output[<Student john>, <Student Sammy>, <Student Carl>]

此时,您的数据库中有三个学生。 接下来,您将为索引页面创建一个 Flask 路由,并在其上显示数据库中的所有学生。

第 3 步 — 显示所有记录

在此步骤中,您将创建一个路线和一个模板,以在索引页面上显示数据库中的所有学生。

让 Flask shell 运行并打开一个新的终端窗口。

打开您的 app.py 文件以向其中添加索引页面的路由:

nano app.py

在文件末尾添加以下路由:

烧瓶应用程序/app.py

# ...

@app.route('/')
def index():
    students = Student.query.all()
    return render_template('index.html', students=students)

保存并关闭文件。

在这里,您使用 app.route() 装饰器创建了一个 index() 视图函数。 在此函数中,您查询数据库并使用具有 query 属性的 Student 模型获取所有学生,这允许您使用不同的方法从数据库中检索一个或多个项目。 您使用 all() 方法获取数据库中的所有学生条目。 您将查询结果存储在名为 students 的变量中,并将其传递给您使用 render_template() 辅助函数呈现的名为 index.html 的模板。

在创建 index.html 模板文件之前,您将在其上显示数据库中的现有学生,您将首先创建一个基本模板,该模板将包含其他模板也将用来避免代码的所有基本 HTML 代码重复。 然后,您将创建在 index() 函数中呈现的 index.html 模板文件。 要了解有关模板的更多信息,请参阅 如何在 Flask 应用程序中使用模板

创建一个 templates 目录,然后打开一个名为 base.html 的新模板:

mkdir templates
nano templates/base.html

base.html 文件中添加以下代码:

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .title {
            margin: 5px;
        }

        .content {
            margin: 5px;
            width: 100%;
            display: flex;
            flex-direction: row;
            flex-wrap: wrap;
        }

        .student {
            flex: 20%;
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3;
            inline-size: 100%;
        }

        .bio {
            padding: 10px;
            margin: 5px;
            background-color: #ffffff;
            color: #004835;
        }

        .name a {
            color: #00a36f;
            text-decoration: none;
        }

        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }
    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="#">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

保存并关闭文件。

此基本模板包含您需要在其他模板中重用的所有 HTML 样板。 title 块将被替换为每个页面设置标题,content 块将被替换为每个页面的内容。 导航栏有三个链接:一个用于索引页面,它使用 url_for() 辅助函数链接到 index() 视图函数,一个用于 Create 页面,还有一个如果您选择向您的应用程序添加一个 About 页面。 您稍后将在添加用于创建新学生的页面以使 Create 链接正常工作后编辑此文件。

接下来,打开一个新的 index.html 模板文件。 这是您在 app.py 文件中引用的模板:

nano templates/index.html

向其中添加以下代码:

flask_app/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h1 class="title">{% block title %} Students {% endblock %}</h1>
    <div class="content">
        {% for student in students %}
            <div class="student">
                <p><b>#{{ student.id }}</b></p>
                <b>
                    <p class="name">{{ student.firstname }} {{ student.lastname }}</p>
                </b>
                <p>{{ student.email }}</p>
                <p>{{ student.age }} years old.</p>
                <p>Joined: {{ student.created_at }}</p>
                <div class="bio">
                    <h4>Bio</h4>
                    <p>{{ student.bio }}</p>
                </div>
            </div>
        {% endfor %}
    </div>
{% endblock %}

保存并关闭文件。

在这里,您扩展基本模板并替换内容块的内容。 您使用的 <h1> 标题也用作标题。 您在 {% for student in students %} 行中使用 Jinja for loop 遍历从 index() 视图函数传递给此的 students 变量中的每个学生模板。 您将显示学生 ID、他们的名字和姓氏、电子邮件、年龄、他们被添加到数据库的日期以及他们的简历。

在激活虚拟环境的 flask_app 目录中,使用 FLASK_APP 环境变量告诉 Flask 应用程序(在本例中为 app.py)。 然后将 FLASK_ENV 环境变量设置为 development 以在开发模式下运行应用程序并访问调试器。 有关 Flask 调试器的更多信息,请参阅 如何处理 Flask 应用程序中的错误 。 使用以下命令执行此操作:

export FLASK_APP=app
export FLASK_ENV=development

接下来,运行应用程序:

flask run

在开发服务器运行的情况下,使用浏览器访问以下 URL:

http://127.0.0.1:5000/

您将在类似于以下内容的页面中看到您添加到数据库中的学生:

您已经在索引页面上显示了数据库中的学生。 接下来,您将为学生页面创建路线,您可以在其中显示每个学生的详细信息。

第 4 步 — 显示单个记录

在这一步中,您将使用 Flask shell 按学生的 ID 查询学生,并创建路由和模板以在专用页面上显示每个学生的详细信息。

在此步骤结束时,URL http://127.0.0.1:5000/1 将成为显示第一个学生的页面(因为它的 ID 为 1)。 URL http://127.0.0.1:5000/ID 将显示带有相关 ID 编号的帖子(如果存在)。

让开发服务器保持运行并打开一个新的终端窗口。

打开 Flask shell 演示如何查询学生:

flask shell

为了从数据库中查询记录和检索数据,Flask-SQLAlchemy 在模型类上提供了一个 query 属性。 您可以使用它的方法来获取具有特定过滤器的记录。

例如,您可以将 filter_by() 方法与 firstname 之类的参数一起使用,该参数将表中的列与参数匹配以检索特定学生:

from app import db, Student
Student.query.filter_by(firstname='Sammy').all()
Output[<Student Sammy>]

在这里,您检索所有名字为 Sammy 的学生。 您使用 all() 方法获取所有结果的列表。 要获得第一个结果,这是这里唯一的结果,您可以使用 first() 方法:

Student.query.filter_by(firstname='Sammy').first()
Output<Student Sammy>

要通过 ID 获取学生,可以使用 filter_by(id=ID)

Student.query.filter_by(id=3).first()

或者,您可以使用较短的 get() 方法,它允许您使用其主键检索特定项目:

Student.query.get(3)

两者都会给出相同的输出:

Output<Student Carl>

您现在可以退出 shell:

exit()

要通过 ID 检索学生,您将创建一个新路由,为每个学生呈现一个页面。 您将使用 Flask-SQLAlchemy 提供的 get_or_404() 方法,它是 get() 方法的变体。 区别在于 get() 在没有结果匹配给定 ID 时返回值 None,而 get_or_404() 返回 404 Not Found HTTP 响应。 打开app.py进行修改:

nano app.py

在文件末尾添加以下路由:

烧瓶应用程序/app.py

# ...

@app.route('/<int:student_id>/')
def student(student_id):
    student = Student.query.get_or_404(student_id)
    return render_template('student.html', student=student)

保存并关闭文件。

在这里,您使用路由 '/<int:student_id>/',其中 int: 是一个 转换器 ,它将 URL 中的默认字符串转换为整数。 student_id 是 URL 变量,它将确定您将在页面上显示的学生。

ID 通过 student_id 参数从 URL 传递到 student() 视图函数。 在函数内部,您使用 get_or_404() 方法查询学生集合并通过 ID 检索学生。 这会将学生数据保存在 student 变量中(如果存在),如果数据库中不存在具有给定 ID 的学生,则返回 404 Not Found HTTP 错误。

您渲染一个名为 student.html 的模板并将其传递给您检索的学生。

打开这个新的 student.html 模板文件:

nano templates/student.html

在这个新的 student.html 文件中键入以下代码。 这将类似于 index.html 模板,只是它只会显示一个学生:

flask_app/templates/student.html

{% extends 'base.html' %}

{% block content %}
    <span class="title">
        <h1>{% block title %} {{ student.firstname }} {{ student.lastname }}{% endblock %}</h1>
    </span>
    <div class="content">
            <div class="student">
                <p><b>#{{ student.id }}</b></p>
                <b>
                    <p class="name">{{ student.firstname }} {{ student.lastname }}</p>
                </b>
                <p>{{ student.email }}</p>
                <p>{{ student.age }} years old.</p>
                <p>Joined: {{ student.created_at }}</p>
                <div class="bio">
                    <h4>Bio</h4>
                    <p>{{ student.bio }}</p>
                </div>
            </div>
    </div>
{% endblock %}

保存并关闭文件。

在此文件中,您扩展了基本模板,将学生的全名设置为页面的标题。 您可以显示学生 ID、学生的名字和姓氏、电子邮件、年龄、记录创建日期以及他们的简历。

使用浏览器导航到第二个学生的 URL:

http://127.0.0.1:5000/2

您将看到类似于以下内容的页面:

现在,编辑 index.html 使每个学生姓名链接到他们的页面:

nano templates/index.html

编辑 for 循环,如下所示:

flask_app/templates/index.html

{% for student in students %}
    <div class="student">
        <p><b>#{{ student.id }}</b></p>
        <b>
            <p class="name">
                <a href="{{ url_for('student', student_id=student.id)}}">
                    {{ student.firstname }} {{ student.lastname }}
                </a>
            </p>
        </b>
        <p>{{ student.email }}</p>
        <p>{{ student.age }} years old.</p>
        <p>Joined: {{ student.created_at }}</p>
        <div class="bio">
            <h4>Bio</h4>
            <p>{{ student.bio }}</p>
        </div>
    </div>
{% endfor %}

保存并关闭文件。

您将 <a> 标记添加到学生的全名,该标记使用 url_for() 函数链接到学生页面,将存储在 student.id 中的学生 ID 传递给 [ X177X] 查看功能。

导航到您的索引页面或刷新它:

http://127.0.0.1:5000/

您现在将看到每个学生姓名都链接到正确的学生页面。

在为个别学生创建页面后,您接下来将添加一个用于将新学生添加到数据库的页面。

第 5 步 — 创建新记录

在此步骤中,您将向应用程序添加一条新路由,以便使用 Web 表单将新学生添加到数据库中。

您将使用 Web 表单呈现一个页面,用户可以在其中输入学生的数据。 然后您将处理表单提交,使用 Student 模型为新学生创建一个对象,将其添加到会话中,然后提交事务,类似于您在步骤 2 中添加学生条目的方式。

让开发服务器保持运行并打开一个新的终端窗口。

首先,打开您的 app.py 文件:

nano app.py

app.py 文件末尾添加以下路由:

烧瓶应用程序/app.py

# ...


@app.route('/create/', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

保存并关闭文件。

在此路由中,您将元组 ('GET', 'POST') 传递给 methods 参数以允许 GET 和 POST 请求。 GET 请求用于从服务器检索数据。 POST 请求用于将数据发布到特定路由。 默认情况下,只允许 GET 请求。 当用户第一次使用 GET 请求请求 /create 路由时,将呈现一个名为 create.html 的模板文件。 您稍后将编辑此路由以处理用户填写和提交 Web 表单以添加新学生时的 POST 请求。

打开新的 create.html 模板:

nano templates/create.html

向其中添加以下代码:

{% extends 'base.html' %}

{% block content %}
    <h1 style="width: 100%">{% block title %} Add a New Student {% endblock %}</h1>
    <form method="post">
        <p>
            <label for="firstname">First Name</label>
            <input type="text" name="firstname"
                   placeholder="First name">
            </input>
        </p>

        <p>
            <label for="lastname">Last Name</label>
            <input type="text" name="lastname"
                   placeholder="Last name">
            </input>
        </p>

        <p>
            <label for="email">Email</label>
            <input type="email" name="email"
                   placeholder="Student email">
            </input>
        </p>

        <p>
            <label for="age">Age</label>
            <input type="number" name="age"
                   placeholder="Age">
            </input>
        </p>

        <p>
        <label for="bio">Bio</label>
        <br>
        <textarea name="bio"
                  placeholder="Bio"
                  rows="15"
                  cols="60"
                  ></textarea>
        </p>
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
{% endblock %}

保存并关闭文件。

您扩展基本模板,将标题设置为标题,并使用 <form> 标记并将属性 method 设置为 post 以指示表单将提交 POST 请求.

您有两个名称为 firstnamelastname 的文本字段。 稍后您将使用这些名称访问用户在您的视图函数中提交的表单数据。

您有一个名为 email 的电子邮件字段、一个学生年龄的数字字段和一个学生简历的文本区域。

最后,表单末尾有一个 Submit 按钮。

现在,随着开发服务器的运行,使用浏览器导航到 /create 路由:

http://127.0.0.1:5000/create

您将看到一个 Add a New Student 页面,其中包含一个 Web 表单和一个 Submit 按钮,如下所示:

如果您填写并提交表单,向服务器发送 POST 请求,则不会发生任何事情,因为您没有处理 /create 路由上的 POST 请求。

打开 app.py 处理用户提交的 POST 请求:

nano app.py

编辑 /create 路线,如下所示:

烧瓶应用程序/app.py

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        firstname = request.form['firstname']
        lastname = request.form['lastname']
        email = request.form['email']
        age = int(request.form['age'])
        bio = request.form['bio']
        student = Student(firstname=firstname,
                          lastname=lastname,
                          email=email,
                          age=age,
                          bio=bio)
        db.session.add(student)
        db.session.commit()

        return redirect(url_for('index'))

    return render_template('create.html')

保存并关闭文件。

您在 if request.method == 'POST' 条件内处理 POST 请求。 您从 request.form 对象中提取用户提交的名字、姓氏、电子邮件、年龄和简历。 您可以使用 int() Python 函数将作为字符串传递的年龄转换为整数。 您使用 Student 模型构造一个 student 对象。 您将学生对象添加到数据库会话,然后提交事务。

最后,您将用户重定向到索引页面,他们可以在其中看到现有学生下方新添加的学生。

在开发服务器运行的情况下,使用浏览器导航到 /create 路由:

http://127.0.0.1:5000/create

用一些数据填写表格并提交。

您将被重定向到索引页面,您将在其中看到新添加的学生。

现在您有了添加新学生的功能,您需要在导航栏中添加指向 Create 页面的链接。 打开base.html

nano templates/base.html

通过修改 Create 链接的 href 属性的值来编辑 <body> 标记:

flask_app/templates/base.html

<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('create') }}">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>

保存并关闭文件。

刷新您的索引页面,您会注意到导航栏中的 Create 链接现在可以使用。

您现在有一个带有用于添加新学生的 Web 表单的页面。 有关 Web 表单的更多信息,请参阅 如何在 Flask 应用程序中使用 Web 表单。 有关管理 Web 表单的更高级和更安全的方法,请参阅 How To Use and Validate Web Forms with Flask-WTF。 接下来,您将添加一个用于编辑现有学生数据的页面。

第 6 步 — 编辑记录

在此步骤中,您将向应用程序添加一个新页面,用于编辑现有学生数据。 您将添加一个新的 /ID/edit/ 路由来根据学生的 ID 编辑学生的数据。

打开app.py

nano app.py

将以下路由添加到文件末尾。 这将获取您要使用其 ID 编辑的学生条目。 它提取通过您稍后将创建的 Web 表单提交的新学生数据。 然后它编辑学生数据,并将用户重定向到索引页面:

烧瓶应用程序/app.py

# ...


@app.route('/<int:student_id>/edit/', methods=('GET', 'POST'))
def edit(student_id):
    student = Student.query.get_or_404(student_id)

    if request.method == 'POST':
        firstname = request.form['firstname']
        lastname = request.form['lastname']
        email = request.form['email']
        age = int(request.form['age'])
        bio = request.form['bio']

        student.firstname = firstname
        student.lastname = lastname
        student.email = email
        student.age = age
        student.bio = bio

        db.session.add(student)
        db.session.commit()

        return redirect(url_for('index'))

    return render_template('edit.html', student=student)

保存并关闭文件。

在这里,您有接受 POST 和 GET 方法的路由 /<int:student_id>/edit/,其中 student_id 作为 URL 变量,将 ID 传递给 edit() 视图函数。

您在 Student 模型上使用 get_or_404() 查询方法来获取与给定学生 ID 关联的学生。 如果数据库中不存在具有给定 ID 的学生,这将返回 404 Not Found 错误。

如果给定的 ID 有与之关联的学生,代码执行将继续到 if request.method == 'POST' 条件。 如果请求是 GET 请求,即用户没有提交表单,则此条件为 false,其中的代码将跳到 return render_template('edit.html', student=student) 行。 这将呈现一个 edit.html 模板,将您从数据库中获得的学生对象传递给它,允许您使用当前学生数据填写学生 Web 表单。 稍后您将创建此 edit.html 模板。

当用户编辑学生数据并提交表单时,会执行 if request.method == 'POST' 中的代码。 您将提交的学生数据从 request.form 对象中提取到相应的变量中。 您将 student 对象的每个属性设置为新提交的数据以更改列值,就像您在步骤 2 中所做的那样。 如果未对 Web 表单上的字段进行更改,则该列的值将在数据库中保持不变。

将学生数据设置为新提交的数据后,将 student 对象添加到数据库会话,然后提交更改。 最后,您将用户重定向到索引页面。

接下来,您需要创建一个用户可以在其中进行编辑的页面。 打开一个新的 edit.html 模板:

nano templates/edit.html

这个新文件将有一个类似于 create.html 文件中的 Web 表单,其中当前学生数据作为字段的默认值。 在其中添加以下代码:

flask_app/templates/edit.html

{% extends 'base.html' %}

{% block content %}
    <h1 style="width: 100%">
        {% block title %} Edit {{ student.firstname }}
                               {{ student.lastname }}'s Details
        {% endblock %}
    </h1>
    <form method="post">
        <p>
            <label for="firstname">First Name</label>
            <input type="text" name="firstname"
                   value={{ student.firstname }}
                   placeholder="First name">
            </input>
        </p>

        <p>
            <label for="lastname">Last Name</label>
            <input type="text" name="lastname"
                   value={{ student.lastname }}
                   placeholder="Last name">
            </input>
        </p>

        <p>
            <label for="email">Email</label>
            <input type="email" name="email"
                   value={{ student.email }}
                   placeholder="Student email">
            </input>
        </p>

        <p>
            <label for="age">Age</label>
            <input type="number" name="age"
                   value={{ student.age }}
                   placeholder="Age">
            </input>
        </p>

        <p>
        <label for="bio">Bio</label>
        <br>
        <textarea name="bio"
                  placeholder="Bio"
                  rows="15"
                  cols="60"
                  >{{ student.bio }}</textarea>
        </p>
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
{% endblock %}

保存并关闭文件。

标题包含学生的名字和姓氏。 每个输入字段的 value 属性和生物文本区域的值设置为您从 edit() 视图函数传递到 student 对象中的对应值X189X] 模板。

现在,导航到以下 URL 以编辑第一个学生的详细信息:

http://127.0.0.1:5000/1/edit

您将看到类似于以下内容的页面:

编辑学生的数据并提交表格。 您将被重定向到索引页面,并且学生的信息将被更新。

接下来,您将在索引页面上的每个学生下方添加一个 Edit 按钮,以链接到他们的编辑页面。 打开index.html模板文件:

nano templates/index.html

编辑此 index.html 文件中的 for 循环,使其看起来与以下内容完全相同:

flask_app/templates/index.html

{% for student in students %}
    <div class="student">
        <p><b>#{{ student.id }}</b></p>
        <b>
            <p class="name">
                <a href="{{ url_for('student', student_id=student.id)}}">
                    {{ student.firstname }} {{ student.lastname }}
                </a>
            </p>
        </b>
        <p>{{ student.email }}</p>
        <p>{{ student.age }} years old.</p>
        <p>Joined: {{ student.created_at }}</p>
        <div class="bio">
            <h4>Bio</h4>
            <p>{{ student.bio }}</p>
        </div>
        <a href="{{ url_for('edit', student_id=student.id) }}">Edit</a>
    </div>
{% endfor %}

保存并关闭文件。

在这里,您添加一个 <a> 标记以链接到 edit() 视图函数,传入 student.id 值以链接到每个学生的编辑页面,并带有 Edit[ X158X] 链接。

您现在有一个用于编辑现有学生的页面。 接下来,您将添加一个 Delete 按钮以从数据库中删除学生。

第 7 步 — 删除记录

在此步骤中,您将添加一条新路线和用于删除现有学生的 Delete 按钮。

首先,您将添加一个接受 POST 请求的新 /id/delete 路由。 您新的 delete() 视图函数将接收您要删除的学生的 ID,将 ID 传递给 Student 模型上的 get_or_404() 查询方法以获取它是否存在,或者如果在数据库中没有找到具有给定 ID 的学生,则返回 404 Not Found 页面。

打开app.py进行编辑:

nano app.py

将以下路由添加到文件末尾:

烧瓶应用程序/app.py

# ...

@app.post('/<int:student_id>/delete/')
def delete(student_id):
    student = Student.query.get_or_404(student_id)
    db.session.delete(student)
    db.session.commit()
    return redirect(url_for('index'))

保存并关闭文件。

在这里,不是使用通常的 app.route 装饰器,而是使用 Flask 版本 2.0.0 中引入的 app.post 装饰器,它为常见的 HTTP 方法添加了快捷方式。 例如,@app.post("/login")@app.route("/login", methods=["POST"]) 的快捷方式。 这意味着这个视图函数只接受 POST 请求,并且在浏览器上导航到 /ID/delete 路由将返回 405 Method Not Allowed 错误,因为 Web 浏览器默认使用 GET 请求。 要删除学生,用户单击一个按钮,该按钮将 POST 请求发送到此路由。

这个 delete() 视图函数通过 student_id URL 变量接收要删除的学生的 ID。 您使用 get_or_404() 方法获取学生并将其保存在 student 变量中,或者在学生不存在的情况下以 404 Not Found 响应。 您在 db.session.delete(student) 行中的数据库会话上使用 delete() 方法,将学生对象传递给它。 这将设置会话以在提交事务时删除学生。 因为您不需要执行任何其他修改,所以您直接使用 db.session.commit() 提交事务。 最后,您将用户重定向到索引页面。

接下来,编辑 index.html 模板以添加 Delete Student 按钮:

nano templates/index.html

通过在 Edit 链接正下方添加一个新的 <form> 标签来编辑 for 循环:

flask_app/templates/index.html

{% for student in students %}
    <div class="student">
        <p><b>#{{ student.id }}</b></p>
        <b>
            <p class="name">
                <a href="{{ url_for('student', student_id=student.id)}}">
                    {{ student.firstname }} {{ student.lastname }}
                </a>
            </p>
        </b>
        <p>{{ student.email }}</p>
        <p>{{ student.age }} years old.</p>
        <p>Joined: {{ student.created_at }}</p>
        <div class="bio">
            <h4>Bio</h4>
            <p>{{ student.bio }}</p>
        </div>
        <a href="{{ url_for('edit', student_id=student.id) }}">Edit</a>

        <hr>
        <form method="POST"
                action="{{ url_for('delete', student_id=student.id) }}">
            <input type="submit" value="Delete Student"
                onclick="return confirm('Are you sure you want to delete this entry?')">
        </form>

    </div>
{% endfor %}

保存并关闭文件。

在这里,您有一个向 delete() 视图函数提交 POST 请求的 Web 表单。 您将 student.id 作为参数传递给 student_id 参数以指定要删除的学生条目。 您可以使用 Web 浏览器中可用的 confirm() 方法 函数在提交请求之前显示确认消息。

现在刷新您的索引页面。

您会在每个学生条目下方看到一个 删除学生 按钮。 单击它,然后确认删除。 您将被重定向到索引页面,学生将不再存在。

您现在可以在学生管理应用程序中从数据库中删除学生。

结论

您构建了一个小型 Flask Web 应用程序,用于使用 Flask 和 Flask-SQLAlchemy 以及 SQLite 数据库来管理学生。 您学习了如何连接到数据库、设置表示表的数据库模型、将项目添加到数据库、查询表以及修改数据库数据。

在您的应用程序中使用 SQLAlchemy 允许您使用 Python 类和对象来管理您的 SQL 数据库。 除了 SQLite,您可以使用另一个数据库引擎,并且除了负责连接的 SQLALCHEMY_DATABASE_URI 配置之外,您不需要更改核心应用程序代码中的任何内容。 这允许您以最少的代码更改从一个 SQL 数据库引擎迁移到另一个 SQL 数据库引擎。 有关详细信息,请参阅 Flask-SQLAlchemy 文档

如果您想了解更多关于 Flask 的信息,请查看 如何使用 Flask 构建 Web 应用程序系列中的其他教程。