> 技术文档 > 【python】Flask 从入门到实战:打造你的专属 Web 应用_flask python

【python】Flask 从入门到实战:打造你的专属 Web 应用_flask python

目录

一、Flask 是什么?为何选择它?

二、环境搭建:开启 Flask 之旅

2.1 安装 Python

2.2 创建虚拟环境

2.3 安装 Flask

三、第一个 Flask 应用:Hello, Flask!

四、深入理解 Flask 核心概念

4.1 应用对象

4.2 路由系统

4.3 请求与响应

五、模板引擎:让页面更具动态性

5.1 Jinja2 模板引擎简介

5.2 在 Flask 中使用 Jinja2 模板

5.3 模板继承与宏

六、表单处理:与用户互动

6.1 创建 HTML 表单

6.2 处理表单数据

6.3 表单验证

七、数据库操作:持久化数据

7.1 SQLAlchemy 简介

7.2 使用 SQLAlchemy 连接数据库

7.3 定义数据库模型

7.4 基本数据库操作

7.4.1 创建表

7.4.2 添加数据

7.4.3 查询数据

7.4.4 更新数据

7.4.5 删除数据

八、项目结构优化:迈向大型项目

8.1 简单项目结构回顾

8.2 中型项目结构示例

8.3 大型项目结构设计

九、总结与展望


一、Flask 是什么?为何选择它?

Flask 是基于 Python 的轻量级 Web 应用框架,诞生于 2010 年,其核心依赖 Werkzeug WSGI 工具包和 Jinja2 模板引擎 。它就像是一个小巧灵活的工具箱,仅提供了 Web 开发所需的核心功能,如简单的路由系统、请求处理和模板渲染等。这种轻量级的设计使得 Flask 非常适合快速搭建小型 Web 应用或进行项目原型开发。

与其他 Python Web 框架相比,Flask 的独特魅力在于它的轻量与灵活。以 Django 为例,Django 是一个功能强大的全栈框架,内置了用户认证、数据库抽象层、表单验证、管理后台等丰富的功能,遵循 “约定优于配置” 的原则,能帮助开发者快速构建功能完备的大型 Web 应用。然而,这种 “大而全” 的特性也使得 Django 相对臃肿,对于一些小型项目或对灵活性要求较高的场景来说,可能会显得过于繁琐,开发者在使用时可能会被框架的预设架构束缚。

而 Flask 则截然不同,它没有过多的内置功能和强制的项目结构,开发者可以根据项目的具体需求自由选择和集成各种扩展和工具。比如,在数据库操作方面,你可以选择 SQLAlchemy 扩展来实现对象关系映射(ORM);在表单处理上,Flask - WTF 扩展能帮助你轻松实现表单验证等功能。这种高度的灵活性让 Flask 在面对各种不同类型和规模的项目时,都能游刃有余,无论是小型的个人博客、简单的 API 服务,还是作为大型复杂项目的基础框架,Flask 都能发挥出其独特的优势。

二、环境搭建:开启 Flask 之旅

在开始使用 Flask 开发 Web 应用之前,我们需要先搭建好开发环境。这包括安装 Python、创建虚拟环境以及安装 Flask。下面将详细介绍在不同操作系统下的搭建步骤。

2.1 安装 Python

Flask 是基于 Python 开发的,所以首先需要安装 Python。建议安装 Python 3.6 及以上版本,因为新版本通常会带来性能优化和新特性,同时也能更好地兼容 Flask 及相关扩展。

  • Windows 系统

从 Python 官方网站(https://www.python.org/downloads/windows/ )下载适合你系统的 Python 安装包(例如,Windows x86-64 executable installer 对于 64 位系统)。下载完成后,双击运行安装程序,在安装过程中,务必勾选 “Add Python to PATH” 选项,这样才能在命令行中直接运行 Python。安装完成后,按下 Win+R 键,打开运行窗口,输入 “cmd” 打开命令行界面,输入 “python -V”(注意有空格),如果看到版本号显示出来(如 Python 3.10.0),则说明 Python 已经成功安装。

  • macOS 系统

可以通过 Homebrew 或直接从 Python 官网下载安装包。如果未安装 Homebrew,通过终端运行以下命令安装:/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\" 。安装好 Homebrew 后,使用brew install python命令安装 Python 3。也可以访问 Python 官网下载对应 macOS 版本的安装包,双击下载的 pkg 文件,跟随安装向导完成安装。安装完成后,打开终端,输入 “python3 --version” 验证 Python 是否安装成功及其版本。

  • Linux 系统

大多数 Linux 发行版的软件库中都包含了 Python。使用包管理器安装是最便捷的方式。例如,Ubuntu/Debian 系统使用以下命令安装:


sudo apt-get update

sudo apt-get install python3

Fedora 系统使用:


sudo dnf install python3

Arch Linux 系统使用:


sudo pacman -Syu python

安装完成后,在终端输入 “python3 --version” 验证安装结果。

2.2 创建虚拟环境

虚拟环境是一种工具,它允许你为不同的项目创建隔离的 Python 运行环境,这样可以避免不同项目之间的依赖包冲突。例如,项目 A 可能依赖 Flask 1.1.2 版本,而项目 B 需要 Flask 2.0.1 版本,使用虚拟环境就可以让这两个项目各自使用自己需要的 Flask 版本,互不干扰。

Python 3.3 及以上版本内置了venv模块用于创建虚拟环境。以创建一个名为myenv的虚拟环境为例,在命令行中执行以下步骤:

  1. 打开命令行工具(在 Windows 上是 CMD 或 PowerShell,在 macOS 或 Linux 上是 Terminal)。
  1. 导航到你想要创建虚拟环境的目录。例如,如果你想在当前用户的文档目录下创建虚拟环境,可以使用cd命令切换到该目录,如cd ~/Documents 。
  1. 运行创建虚拟环境的命令:python -m venv myenv 。这里的myenv是你为虚拟环境指定的名称,你可以根据实际情况修改。

创建好虚拟环境后,需要激活它才能使用。激活虚拟环境的方式因操作系统而异:

  • Windows 系统

在命令行中进入虚拟环境的 Scripts 目录,然后执行activate命令。例如,如果你的虚拟环境名为myenv,则执行myenv\\Scripts\\activate 。激活后,命令行提示符会显示虚拟环境的名称,如(myenv) C:\\Users\\YourUsername\\Documents> ,表示你现在在该虚拟环境中工作。

  • macOS 和 Linux 系统

在命令行中执行source myenv/bin/activate 。激活后,命令行提示符同样会显示虚拟环境的名称,如(myenv) yourusername@yourcomputer:~$ 。

2.3 安装 Flask

激活虚拟环境后,就可以使用 Python 的包管理器pip来安装 Flask 了。在命令行中输入以下命令:


pip install Flask

pip会自动从 Python Package Index(PyPI)下载并安装 Flask 及其依赖项,如itsdangerous、Jinja2、blinker、Werkzeug、click、importlib - metadata等。安装过程中,pip会显示安装进度和相关信息。安装完成后,可以通过以下命令验证 Flask 是否安装成功:


pip show Flask

如果安装成功,会显示 Flask 的相关信息,包括名称、版本、摘要、主页、作者、作者邮箱、许可证、安装位置以及依赖项等,类似如下:


Name: Flask

Version: 3.0.3

Summary: A simple framework for building complex web applications.

Home-page:

Author:

Author-email:

License:

Location: /Users/RUNOOB/.pyenv/versions/3.9.7/lib/python3.9/site-packages

Requires: itsdangerous, Jinja2, blinker, Werkzeug, click, importlib-metadata

Required-by:

至此,我们已经成功搭建好了 Flask 的开发环境,接下来就可以正式开始 Flask 的学习和实践了。

三、第一个 Flask 应用:Hello, Flask!

现在我们已经搭建好了开发环境,接下来就动手创建我们的第一个 Flask 应用吧!这个应用非常简单,当用户访问特定的 URL 时,它会返回 “Hello, Flask!” 的消息。

在你喜欢的代码编辑器中,创建一个新的 Python 文件,命名为app.py(当然,你也可以使用其他名称,但app.py是 Flask 项目的常见命名)。然后,在文件中输入以下代码:


from flask import Flask

# 创建Flask应用实例

app = Flask(__name__)

# 定义路由和视图函数

@app.route(\'/\')

def hello():

return \'Hello, Flask!\'

if __name__ == \'__main__\':

app.run(debug=True)

让我们逐行分析这段代码:

  1. 导入 Flask 模块:from flask import Flask,这行代码从flask库中导入Flask类。Flask类是 Flask 框架的核心,我们通过实例化这个类来创建我们的 Web 应用。
  1. 创建 Flask 应用实例:app = Flask(__name__) ,这里使用Flask类创建了一个名为app的应用实例。__name__是 Python 的内置变量,它代表当前模块的名称。在这个例子中,__name__的值是\'__main__\' ,因为我们是直接运行这个脚本。Flask 应用实例需要知道它所在的模块,以便正确地加载资源和配置。
  1. 定义路由和视图函数
    • @app.route(\'/\')是一个装饰器,它定义了一个路由规则。\'/\'表示根 URL,当用户访问应用的根 URL(例如http://127.0.0.1:5000/ )时,会触发下面的函数。
    • def hello():定义了一个视图函数,视图函数负责处理用户的请求并返回响应。在这个例子中,hello函数返回字符串\'Hello, Flask!\' ,当用户访问根 URL 时,这个字符串就会作为响应内容返回给用户。
  1. 运行应用
    • if __name__ == \'__main__\': 这是 Python 的惯用法,它确保只有在直接运行这个脚本时,才会执行下面的代码,而当这个脚本被作为模块导入到其他脚本中时,不会执行。
    • app.run(debug=True) 启动 Flask 开发服务器,debug=True表示开启调试模式。在调试模式下,Flask 会提供详细的错误信息,并且当代码发生变化时,服务器会自动重新加载,方便我们开发和调试。

保存app.py文件,然后在命令行中进入到该文件所在的目录(确保你的虚拟环境已经激活),输入以下命令运行应用:


python app.py

运行成功后,你会看到类似如下的输出:


* Serving Flask app \'app\'

* Debug mode: on

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.

* Running on http://127.0.0.1:5000

Press CTRL+C to quit

* Restarting with stat

* Debugger is active!

* Debugger PIN: 123-456-789

这表明你的 Flask 应用已经在http://127.0.0.1:5000地址上运行起来了,并且处于调试模式。打开你的浏览器,访问http://127.0.0.1:5000 ,你应该能看到页面上显示 “Hello, Flask!”。恭喜你,你已经成功创建并运行了你的第一个 Flask 应用!

四、深入理解 Flask 核心概念

4.1 应用对象

在 Flask 中,Flask类的实例就是我们的应用对象,它代表整个 Web 应用。如我们在第一个 Flask 应用中创建的app = Flask(__name__) ,这里的app就是应用对象。它负责管理整个应用的配置、路由、请求处理等核心功能。应用对象是 Flask 应用的入口点,所有的请求都会通过它来进行分发和处理 。它还提供了一系列方法和属性,方便我们对应用进行定制和扩展。例如,app.run()方法用于启动 Flask 开发服务器,app.config属性用于配置应用的各种参数,如密钥、数据库连接字符串等。

4.2 路由系统

路由系统是 Flask 的核心功能之一,它负责将不同的 URL 映射到对应的视图函数上,使得应用能够根据用户请求的 URL 来执行相应的代码逻辑 。在 Flask 中,使用@app.route装饰器来定义路由。例如:


@app.route(\'/\')

def index():

return \'This is the index page\'

@app.route(\'/about\')

def about():

return \'This is the about page\'

在上述代码中,@app.route(\'/\')将根 URL 映射到index函数,当用户访问http://127.0.0.1:5000/时,会执行index函数并返回相应的内容;@app.route(\'/about\')将/about URL 映射到about函数,当用户访问http://127.0.0.1:5000/about时,会执行about函数。

除了静态路由,Flask 还支持动态路由,即 URL 中可以包含参数。动态路由的参数使用的形式定义,视图函数中可以接收这些参数。例如,要创建一个显示用户个人信息的页面,每个用户有不同的 ID,我们可以这样定义动态路由:


@app.route(\'/user/\')

def user_profile(user_id):

return f\'This is the profile page of user {user_id}\'

这里的表示这是一个动态参数,类型为整数,参数名为user_id。当用户访问http://127.0.0.1:5000/user/1时,user_id的值为 1,视图函数user_profile会接收到这个值并返回相应的内容。如果访问http://127.0.0.1:5000/user/abc ,由于abc不是整数,Flask 会返回 404 错误,因为它无法匹配到对应的路由。

Flask 支持的动态路由参数类型除了int(整数),还有string(字符串,默认类型,可不写类型声明)、float(浮点数)、path(路径,可包含斜杠)等。例如:


@app.route(\'/item/\')

def show_item(item_name):

return f\'This is the item: {item_name}\'

@app.route(\'/price/\')

def show_price(price):

return f\'The price is {price}\'

@app.route(\'/file/\')

def show_file(file_path):

return f\'The file path is {file_path}\'

在实际应用中,动态路由非常有用。比如在一个博客系统中,可以通过动态路由来展示不同文章的详情页面,根据文章的 ID 来区分不同的文章;在一个电商系统中,可以根据商品 ID 来展示不同商品的详情页。

4.3 请求与响应

在 Web 开发中,请求与响应是客户端(通常是浏览器)和服务器之间交互的基本方式。当用户在浏览器中输入 URL 并访问时,浏览器会向服务器发送一个 HTTP 请求,服务器接收到请求后进行处理,然后返回一个 HTTP 响应给浏览器。

在 Flask 中,处理请求和返回响应是通过视图函数来完成的。Flask 会自动将客户端发送的请求信息封装成request对象,视图函数可以通过这个对象获取请求的各种信息,如请求方法(GET、POST 等)、请求参数、请求头、请求体等 。例如,要获取 URL 中的查询参数,可以使用request.args.get(\'参数名\') ;要获取 POST 请求中的表单数据,可以使用request.form.get(\'参数名\') ;如果请求的是 JSON 数据,可以使用request.json来获取解析后的 JSON 数据。示例代码如下:


from flask import Flask, request

app = Flask(__name__)

@app.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\':

return \'Login successful\'

else:

return \'Login failed\'

return \'\'\'



\'\'\'

在上述代码中,/login路由支持 GET 和 POST 方法。当请求方法为 POST 时,从request.form中获取username和password参数,进行登录验证,然后返回相应的结果;当请求方法为 GET 时,返回一个包含登录表单的 HTML 页面。

视图函数处理完请求后,需要返回一个响应给客户端。响应可以是字符串(会被作为文本响应返回)、HTML 页面(通过模板渲染后返回)、JSON 数据(使用jsonify函数将数据转换为 JSON 格式的响应)、Response对象(可以自定义响应头、状态码等)等。例如:


from flask import Flask, jsonify

app = Flask(__name__)

@app.route(\'/data\')

def get_data():

data = {\'name\': \'John\', \'age\': 30}

return jsonify(data)

在这个例子中,get_data函数返回一个 JSON 格式的响应,jsonify函数会自动设置响应头的Content-Type为application/json ,方便客户端解析数据。如果需要返回自定义状态码和响应头,可以使用Response对象,示例如下:


from flask import Flask, Response

app = Flask(__name__)

@app.route(\'/custom_response\')

def custom_response():

response = Response(\'This is a custom response\', status=201, headers={\'X-Custom-Header\': \'Value\'})

return response

这里创建了一个Response对象,设置响应体为\'This is a custom response\' ,状态码为 201,并且添加了一个自定义的响应头X-Custom-Header ,其值为\'Value\' 。

五、模板引擎:让页面更具动态性

在 Web 开发中,我们常常需要根据不同的用户请求或数据生成动态的 HTML 页面,这就离不开模板引擎的帮助。Flask 默认使用 Jinja2 作为模板引擎,它功能强大且灵活,能够很好地满足 Web 开发中动态页面生成的需求。

5.1 Jinja2 模板引擎简介

Jinja2 是一个用 Python 编写的模板引擎,它的设计思想借鉴了 Django 的模板引擎,并在此基础上进行了扩展,提供了更加丰富和强大的功能。Jinja2 的主要特点包括:

  • 简洁的语法:采用类似于 Python 的语法,易于学习和使用。例如,使用{{ variable }}来输出变量,{% if condition %}... {% endif %}来进行条件判断,{% for item in list %}... {% endfor %}来进行循环等。
  • 强大的控制结构:支持完整的 Python 控制结构,如条件语句(if-else)、循环语句(for、while)等,使得在模板中可以进行复杂的逻辑处理 。
  • 丰富的过滤器:过滤器用于在渲染变量之前对其进行处理和转换,Jinja2 内置了大量常用的过滤器,如capitalize(将字符串首字母大写)、lower(将字符串转换为小写)、upper(将字符串转换为大写)、trim(去除字符串两端的空白字符)、join(将列表元素连接成字符串)、format(格式化字符串)等。通过管道符|来应用过滤器,例如{{ name | capitalize }} 。
  • 模板继承:允许创建一个基础模板,然后其他模板可以继承这个基础模板,并根据需要覆盖其中的部分内容。这在创建具有统一布局和风格的 Web 应用时非常有用,可以大大减少重复代码 。
  • 宏定义:类似于 Python 中的函数,宏允许在模板中定义可重用的代码块,通过传递不同的参数来实现不同的功能 。

5.2 在 Flask 中使用 Jinja2 模板

在 Flask 中使用 Jinja2 模板非常简单,Flask 提供了render_template函数来渲染模板。假设我们要创建一个简单的博客应用,展示文章列表。首先,在项目目录下创建一个templates文件夹(Flask 会自动从这个文件夹中查找模板文件),然后在其中创建一个index.html模板文件,内容如下:


My Blog

My Blog

    {% for post in posts %}

  • <a href=\"{{ post.url }}\">{{ post.title }} - {{ post.author }} - {{ post.published_date }}

  • {% endfor %}

在上述模板中,{{ post.title }}、{{ post.author }}、{{ post.published_date }}等是变量占位符,它们将在渲染时被实际的数据替换;{% for post in posts %}和{% endfor %}是循环控制结构,用于遍历posts列表并为每个文章项生成一个

  • 元素 。

    接下来,在 Flask 应用中,我们需要准备数据并调用render_template函数来渲染这个模板。在app.py中添加如下代码:

    
    

    from flask import Flask, render_template

    app = Flask(__name__)

    # 模拟文章数据

    posts = [

    {

    \'title\': \'First Post\',

    \'author\': \'John\',

    \'published_date\': \'2024-10-01\',

    \'url\': \'/posts/1\'

    },

    {

    \'title\': \'Second Post\',

    \'author\': \'Jane\',

    \'published_date\': \'2024-10-02\',

    \'url\': \'/posts/2\'

    }

    ]

    @app.route(\'/\')

    def index():

    return render_template(\'index.html\', posts=posts)

    if __name__ == \'__main__\':

    app.run(debug=True)

    在index视图函数中,我们将posts列表作为参数传递给render_template函数,这样在index.html模板中就可以使用posts变量了。运行 Flask 应用后,访问http://127.0.0.1:5000/,就可以看到渲染后的页面,其中展示了文章列表。

    5.3 模板继承与宏

    模板继承是 Jinja2 的一个重要特性,它允许我们创建一个基础模板(通常包含网站的通用结构,如页眉、页脚、导航栏等),然后其他模板可以继承这个基础模板,并覆盖其中的特定块,从而实现代码的复用和页面结构的统一 。

    例如,创建一个基础模板base.html:

    
    

    {% block title %}My Site{% endblock %}

    My Awesome Site

    {% block content %}{% endblock %}

    © 2024 My Company

    在这个基础模板中,{% block title %}{% endblock %}和{% block content %}{% endblock %}定义了两个可替换的块,子模板可以根据需要覆盖这些块的内容 。

    然后,创建一个继承自base.html的子模板index.html:

    
    

    {% extends \"base.html\" %}

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

    {% block content %}

    Welcome to my home page

    This is the content of the home page.

    {% endblock %}

    在index.html中,{% extends \"base.html\" %}表示继承base.html模板,{% block title %}和{% block content %}分别覆盖了base.html中相应块的内容 。

    宏则是 Jinja2 中可重用的模板代码块,类似于函数。例如,在templates文件夹中创建一个macros.html文件,定义一个用于渲染表单输入框的宏:

    
    

    {% macro render_input(name, type=\'text\', value=\'\') %}

    <input type=\"{{ type }}\" name=\"{{ name }}\" value=\"{{ value }}\">

    {% endmacro %}

    在其他模板中,可以通过{% from \"macros.html\" import render_input %}导入这个宏,然后使用{{ render_input(\'username\') }}这样的方式来调用宏,传入不同的参数来生成不同的表单输入框 。

    六、表单处理:与用户互动

    在 Web 应用中,表单是实现用户与服务器交互的重要方式,用户可以通过表单输入数据,如注册信息、登录凭证、搜索关键词等,然后将这些数据提交给服务器进行处理。Flask 提供了基本的表单处理功能,结合一些扩展库(如 Flask - WTF),可以更方便地实现表单验证、数据获取等操作 。

    6.1 创建 HTML 表单

    首先,我们需要在 HTML 中创建表单。表单的method属性通常设置为POST(也可以是GET,但POST更安全,适合提交敏感数据或大数据量的表单),action属性指定表单数据提交的目标 URL,通常是 Flask 应用中的某个路由 。

    例如,创建一个简单的登录表单login.html:

    
    

    Login

    Login

    <form action=\"{{ url_for(\'login\') }}\" method=\"post\">



    在这个表单中,action=\"{{ url_for(\'login\') }}\"表示表单数据将提交到名为login的 Flask 路由;method=\"post\"指定使用 POST 方法提交表单;required属性是 HTML5 的验证属性,确保用户在提交表单时必须填写这两个字段,否则浏览器会弹出提示框阻止表单提交 。

    6.2 处理表单数据

    在 Flask 应用中,需要定义一个路由来接收表单数据,并在相应的视图函数中处理这些数据。通过request对象的form属性可以获取 POST 请求中的表单数据 。

    在app.py中添加如下代码:

    
    

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

    app = Flask(__name__)

    @app.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\':

    return redirect(url_for(\'dashboard\'))

    else:

    return \'Login failed. Please check your username and password.\'

    return render_template(\'login.html\')

    @app.route(\'/dashboard\')

    def dashboard():

    return \'Welcome to the dashboard, admin!\'

    if __name__ == \'__main__\':

    app.run(debug=True)

    在login视图函数中,首先判断请求方法是否为 POST,如果是,则从request.form中获取username和password字段的值。然后进行简单的用户认证,若认证成功,使用redirect函数重定向到dashboard路由;若认证失败,返回错误提示信息。如果请求方法是 GET,则直接渲染login.html模板,显示登录表单。

    6.3 表单验证

    表单验证是确保用户输入数据有效性的重要环节,可以防止非法数据进入系统,提高系统的安全性和稳定性 。虽然 Flask 本身不提供强大的表单验证功能,但结合 WTForms 库(通常通过 Flask - WTF 扩展使用),可以很方便地实现表单验证。

    安装 Flask - WTF 扩展:

    
    

    pip install flask-wtf

    在app.py中定义一个表单类,并使用验证器来验证表单字段:

    
    

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

    from flask_wtf import FlaskForm

    from wtforms import StringField, PasswordField, SubmitField

    from wtforms.validators import DataRequired, Length

    app = Flask(__name__)

    app.secret_key = \'your_secret_key\' # 用于CSRF保护,必须设置

    class LoginForm(FlaskForm):

    username = StringField(\'Username\', validators=[DataRequired(), Length(min=3, max=20)])

    password = PasswordField(\'Password\', validators=[DataRequired(), Length(min=6, max=50)])

    submit = SubmitField(\'Login\')

    @app.route(\'/login\', methods=[\'GET\', \'POST\'])

    def login():

    form = LoginForm()

    if form.validate_on_submit():

    username = form.username.data

    password = form.password.data

    # 进行用户认证等处理

    if username == \'admin\' and password == \'123456\':

    return redirect(url_for(\'dashboard\'))

    else:

    return \'Login failed. Please check your username and password.\'

    return render_template(\'login.html\', form=form)

    @app.route(\'/dashboard\')

    def dashboard():

    return \'Welcome to the dashboard, admin!\'

    if __name__ == \'__main__\':

    app.run(debug=True)

    在上述代码中,LoginForm类继承自FlaskForm,定义了username、password和submit三个字段。DataRequired验证器确保字段不为空,Length验证器限制了字段的长度范围。在login视图函数中,使用form.validate_on_submit()方法来验证表单数据,如果验证通过,则进行后续处理;如果验证失败,form对象会包含错误信息,这些错误信息可以在模板中显示给用户 。

    在login.html模板中,可以通过form.errors来显示错误信息:

    
    

    Login

    Login

    {{ form.hidden_tag() }}

    {{ form.username.label }}: {{ form.username() }}

    {% if form.username.errors %}

      {% for error in form.username.errors %}

    • {{ error }}
    • {% endfor %}

    {% endif %}

    {{ form.password.label }}: {{ form.password() }}

    {% if form.password.errors %}

      {% for error in form.password.errors %}

    • {{ error }}
    • {% endfor %}

    {% endif %}

    {{ form.submit() }}

    {{ form.hidden_tag() }}用于生成一个隐藏的 CSRF 令牌,防止跨站请求伪造攻击 。当表单验证失败时,会在相应字段下方显示错误信息,帮助用户了解输入错误并进行修正。

    七、数据库操作:持久化数据

    在实际的 Web 应用中,数据的持久化存储是至关重要的,数据库就是实现这一功能的关键工具。Flask 本身并没有内置的数据库处理模块,但它提供了很好的扩展性,我们可以通过使用各种数据库扩展来实现与不同类型数据库的交互,其中 SQLAlchemy 是一个非常流行且强大的选择 。

    7.1 SQLAlchemy 简介

    SQLAlchemy 是 Python 的一个数据库抽象层库,它提供了一套丰富的工具和 API,让开发者可以使用 Python 代码与各种关系型数据库(如 MySQL、PostgreSQL、SQLite 等)进行交互,而无需直接编写 SQL 语句,这种方式被称为对象关系映射(ORM) 。ORM 的核心思想是将数据库中的表映射为 Python 中的类,表中的行映射为类的实例,表中的列映射为类的属性,这样我们就可以通过操作 Python 对象来实现对数据库的增删改查等操作,大大提高了开发效率和代码的可维护性 。

    7.2 使用 SQLAlchemy 连接数据库

    首先,需要安装flask_sqlalchemy扩展,它是专门为 Flask 应用集成 SQLAlchemy 而设计的,简化了在 Flask 中使用 SQLAlchemy 的配置和操作。使用pip命令进行安装:

    
    

    pip install flask_sqlalchemy

    安装完成后,在 Flask 应用中进行配置。假设我们使用 SQLite 数据库,在app.py中添加如下代码:

    
    

    from flask import Flask

    from flask_sqlalchemy import SQLAlchemy

    app = Flask(__name__)

    # 配置数据库连接,这里使用SQLite,数据库文件名为test.db

    app.config[\'SQLALCHEMY_DATABASE_URI\'] =\'sqlite:///test.db\'

    # 关闭对模型修改的监控,以减少不必要的开销

    app.config[\'SQLALCHEMY_TRACK_MODIFICATIONS\'] = False

    # 创建SQLAlchemy实例

    db = SQLAlchemy(app)

    在上述代码中:

    • app.config[\'SQLALCHEMY_DATABASE_URI\']设置了数据库的连接字符串,sqlite:///test.db表示使用 SQLite 数据库,数据库文件名为test.db,如果文件不存在,SQLAlchemy 会自动创建。
    • app.config[\'SQLALCHEMY_TRACK_MODIFICATIONS\'] = False用于关闭 Flask - SQLAlchemy 对模型修改的监控,因为这个功能会消耗额外的内存和性能,在生产环境中如果不需要实时追踪模型的修改情况,可以将其关闭 。
    • db = SQLAlchemy(app)创建了一个 SQLAlchemy 实例,这个实例将用于定义数据库模型和执行数据库操作 。

    如果要连接其他类型的数据库,如 MySQL,连接字符串的格式会有所不同。假设 MySQL 数据库的用户名是root,密码是123456,主机是localhost,端口是3306,数据库名为mydb,则连接字符串如下:

    
    

    app.config[\'SQLALCHEMY_DATABASE_URI\'] =\'mysql://root:123456@localhost:3306/mydb\'

    这里需要注意,连接 MySQL 数据库还需要安装 MySQL 的 Python 驱动,如mysqlclient或pymysql,可以使用pip install mysqlclient或pip install pymysql进行安装 。

    7.3 定义数据库模型

    数据库模型是数据库表在 Python 中的映射,通过定义模型类,我们可以方便地操作数据库表。模型类需要继承自db.Model,其中的属性对应数据库表中的列 。

    例如,我们要创建一个用户表User,包含id(主键,自增长)、username(用户名,字符串类型,唯一且不能为空)、email(邮箱,字符串类型,唯一且不能为空)字段,在app.py中添加如下代码:

    
    

    class User(db.Model):

    __tablename__ = \'user\' # 定义表名,若不指定,默认使用类名的小写形式作为表名

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    username = db.Column(db.String(80), unique=True, nullable=False)

    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):

    return f\'\'

    在上述代码中:

    • __tablename__指定了数据库表名,如果不设置,SQLAlchemy 会自动将类名的小写形式作为表名 。
    • db.Column用于定义表中的列,第一个参数指定列的数据类型,如db.Integer表示整数类型,db.String(80)表示最大长度为 80 的字符串类型;后面的参数primary_key=True表示该列是主键,autoincrement=True表示主键自增长,unique=True表示该列的值必须唯一,nullable=False表示该列不能为空 。
    • __repr__方法是一个特殊的方法,用于定义对象的字符串表示形式,方便在调试和打印对象时查看对象的信息 。

    7.4 基本数据库操作

    定义好数据库模型后,就可以进行基本的数据库操作了,如创建表、添加数据、查询数据、更新数据和删除数据,这些操作通常被称为 CRUD(Create, Read, Update, Delete)操作 。

    7.4.1 创建表

    在应用启动时,需要创建数据库表。可以使用db.create_all()方法来创建所有定义的模型类对应的表。通常在app.py中添加如下代码:

    
    

    with app.app_context():

    db.create_all()

    with app.app_context()用于创建一个应用上下文,在这个上下文中才能正确地执行数据库操作,db.create_all()会根据定义的模型类在数据库中创建相应的表 。如果数据库表已经存在,再次执行db.create_all()不会覆盖已有的表,而是会跳过创建操作 。

    7.4.2 添加数据

    添加数据需要先创建模型类的实例,然后将其添加到数据库会话(db.session)中,最后提交会话,将数据保存到数据库 。例如,添加一个新用户:

    
    

    @app.route(\'/add_user\')

    def add_user():

    new_user = User(username=\'John\', email=\'john@example.com\')

    db.session.add(new_user)

    db.session.commit()

    return \'User added!\'

    在上述代码中:

    • new_user = User(username=\'John\', email=\'john@example.com\')创建了一个User模型类的实例,设置了username和email属性。
    • db.session.add(new_user)将新用户对象添加到数据库会话中。
    • db.session.commit()提交会话,将新用户数据保存到数据库中。如果在添加数据过程中出现错误,如违反了唯一约束(username或email已存在),会抛出异常,并且事务会回滚,数据不会被保存到数据库 。
    7.4.3 查询数据

    查询数据使用模型类的query属性,它提供了一系列方法来执行不同类型的查询操作 。

    • 查询所有数据:使用query.all()方法可以获取表中的所有记录。例如:
    
    

    @app.route(\'/get_users\')

    def get_users():

    users = User.query.all()

    user_list = []

    for user in users:

    user_info = {

    \'id\': user.id,

    \'username\': user.username,

    \'email\': user.email

    }

    user_list.append(user_info)

    return {\'users\': user_list}

    在上述代码中,User.query.all()获取了User表中的所有用户记录,然后将每个用户的信息整理成字典形式,添加到列表中,最后返回包含所有用户信息的字典 。

    • 条件查询:使用query.filter_by()方法可以根据指定条件查询数据。例如,查询用户名为John的用户:
    
    

    @app.route(\'/get_user_by_username\')

    def get_user_by_username():

    user = User.query.filter_by(username=\'John\').first()

    if user:

    return {

    \'id\': user.id,

    \'username\': user.username,

    \'email\': user.email

    }

    else:

    return \'User not found\'

    User.query.filter_by(username=\'John\').first()表示查询username为John的用户记录,并返回第一个匹配的结果(因为first()方法只返回第一个结果)。如果没有找到匹配的用户,user的值为None 。

    除了filter_by()方法,还可以使用filter()方法进行更灵活的条件查询,filter()方法支持使用各种 SQL 表达式和函数,例如:

    
    

    from sqlalchemy import or_

    @app.route(\'/get_users_by_condition\')

    def get_users_by_condition():

    users = User.query.filter(or_(User.username == \'John\', User.email.endswith(\'@example.com\'))).all()

    user_list = []

    for user in users:

    user_info = {

    \'id\': user.id,

    \'username\': user.username,

    \'email\': user.email

    }

    user_list.append(user_info)

    return {\'users\': user_list}

    在这个例子中,使用or_函数实现了复杂的查询条件,查询用户名为John或者邮箱以@example.com结尾的用户记录 。

    7.4.4 更新数据

    更新数据需要先查询出要更新的记录,然后修改其属性值,最后提交会话 。例如,将用户John的邮箱更新为new_email@example.com:

    
    

    @app.route(\'/update_user\')

    def update_user():

    user = User.query.filter_by(username=\'John\').first()

    if user:

    user.email = \'new_email@example.com\'

    db.session.commit()

    return \'User updated!\'

    else:

    return \'User not found\'

    在上述代码中,先查询出用户名为John的用户记录,然后修改其email属性,最后通过db.session.commit()提交更改,将更新后的数据保存到数据库 。如果在更新过程中出现错误,如违反了唯一约束,事务会回滚,数据不会被更新 。

    7.4.5 删除数据

    删除数据同样需要先查询出要删除的记录,然后将其从数据库会话中删除,最后提交会话 。例如,删除用户John:

    
    

    @app.route(\'/delete_user\')

    def delete_user():

    user = User.query.filter_by(username=\'John\').first()

    if user:

    db.session.delete(user)

    db.session.commit()

    return \'User deleted!\'

    else:

    return \'User not found\'

    在上述代码中,先查询出用户名为John的用户记录,然后使用db.session.delete(user)将其从数据库会话中删除,最后通过db.session.commit()提交删除操作,将用户从数据库中删除 。如果在删除过程中出现错误,事务会回滚,用户不会被删除 。

    八、项目结构优化:迈向大型项目

    当我们的 Flask 项目规模逐渐增大,功能不断丰富时,一个良好的项目结构就显得尤为重要。合理的项目结构不仅能提高代码的可读性、可维护性,还能方便团队协作开发,避免代码混乱和冲突 。

    8.1 简单项目结构回顾

    在前面的学习中,我们创建的第一个 Flask 应用非常简单,项目结构也很单一,只有一个app.py文件,所有的代码都集中在这个文件中,如下所示:

    
    

    my_flask_app/

    ├── app.py

    └── requirements.txt

    其中,app.py包含了 Flask 应用的核心代码,如创建应用实例、定义路由和视图函数等;requirements.txt记录了项目所依赖的库及其版本信息,方便在不同环境中安装项目依赖 。这种简单的结构适用于小型项目或快速原型开发,但当项目变得复杂时,将所有代码放在一个文件中会使代码难以管理和维护 。

    8.2 中型项目结构示例

    对于稍复杂的应用,我们可以将项目分为多个模块和目录,采用如下的项目结构:

    
    

    my_flask_app/

    ├── app/

    │ ├── __init__.py

    │ ├── routes.py

    │ └── models.py

    ├── config.py

    ├── requirements.txt

    └── run.py

    在这个结构中:

    • app/目录:是 Flask 应用的核心代码目录,包含了应用的主要逻辑。
      • __init__.py:用于初始化 Flask 应用,通常会创建 Flask 应用实例,并配置一些扩展(如 SQLAlchemy 等),还可以导入和注册蓝图 。
      • routes.py:专门用于定义应用的路由和视图函数,将不同功能的路由集中管理,提高代码的可读性和可维护性 。例如:
    
    

    from flask import Blueprint

    bp = Blueprint(\'main\', __name__)

    @bp.route(\'/\')

    def home():

    return \'This is the home page\'

    @bp.route(\'/about\')

    def about():

    return \'This is the about page\'

    这里使用Blueprint创建了一个名为main的蓝图,定义了根路由和/about路由及其对应的视图函数 。然后在__init__.py中注册这个蓝图:

    
    

    from flask import Flask

    from .routes import bp

    app = Flask(__name__)

    app.register_blueprint(bp)

    • models.py:用于定义应用的数据模型,与数据库相关的操作都可以在这里进行定义。例如,如果使用 SQLAlchemy,会在这里定义数据库表对应的模型类,如:
    
    

    from app import db

    class User(db.Model):

    __tablename__ = \'user\'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    username = db.Column(db.String(80), unique=True, nullable=False)

    email = db.Column(db.String(120), unique=True, nullable=False)

    • config.py文件:用于存放应用的配置信息,如数据库连接字符串、密钥、调试模式开关等 。例如:
    
    

    class Config:

    SECRET_KEY = \'your_secret_key\'

    SQLALCHEMY_DATABASE_URI =\'sqlite:///test.db\'

    SQLALCHEMY_TRACK_MODIFICATIONS = False

    • requirements.txt文件:与简单项目结构中的作用相同,记录项目的依赖库及其版本,方便在新环境中安装依赖 。
    • run.py文件:是项目的启动文件,通过运行这个文件来启动 Flask 应用 。内容如下:
    
    

    from app import create_app

    app = create_app()

    if __name__ == \'__main__\':

    app.run(debug=True)

    这种中型项目结构将不同功能的代码分离到不同的文件和模块中,使得代码结构更加清晰,易于维护和扩展 。例如,当需要添加新的路由时,只需在routes.py中进行添加;当需要修改数据模型时,在models.py中进行操作即可 。

    8.3 大型项目结构设计

    对于大型应用,可能需要更复杂、更模块化的项目结构,以支持更高的扩展性和团队协作开发 。常见的大型 Flask 项目结构如下:

    
    

    my_flask_app/

    ├── app/

    │ ├── __init__.py

    │ ├── routes/

    │ │ ├── __init__.py

    │ │ ├── main.py

    │ │ └── auth.py

    │ ├── models/

    │ │ ├── __init__.py

    │ │ └── user.py

    │ ├── services/

    │ │ ├── __init__.py

    │ │ └── user_service.py

    │ ├── templates/

    │ │ ├── layout.html

    │ │ ├── home/

    │ │ │ └── index.html

    │ │ └── auth/

    │ │ └── login.html

    │ └── static/

    │ ├── css/

    │ └── js/

    ├── config/

    │ ├── __init__.py

    │ └── settings.py

    ├── migrations/

    │ └── ...

    ├── tests/

    │ ├── __init__.py

    │ ├── test_routes.py

    │ └── test_models.py

    ├── requirements.txt

    └── run.py

    在这个结构中:

    • app/目录:依然是应用的核心代码目录,但进一步细化了模块结构。
      • __init__.py:除了初始化 Flask 应用和注册蓝图外,还可以进行一些全局的设置和初始化操作 。
      • routes/目录:将不同功能模块的路由进一步分开管理,例如main.py中存放主业务模块的路由,auth.py中存放认证相关的路由 。以auth.py为例:
    
    

    from flask import Blueprint, render_template, request, redirect, url_for

    bp = Blueprint(\'auth\', __name__)

    @bp.route(\'/login\', methods=[\'GET\', \'POST\'])

    def login():

    if request.method == \'POST\':

    # 处理登录逻辑

    return redirect(url_for(\'main.dashboard\'))

    return render_template(\'auth/login.html\')

    • models/目录:存放各种数据模型类,每个模型类可以放在单独的文件中,如user.py中定义用户模型 。
    • services/目录:用于存放业务逻辑相关的代码,将业务逻辑从路由和模型中分离出来,提高代码的复用性和可维护性 。例如,user_service.py中可以定义与用户相关的业务逻辑方法,如用户注册、登录验证等 。
    • templates/目录:存放 HTML 模板文件,按照功能模块进一步细分目录,如home/目录下存放主页相关的模板,auth/目录下存放认证相关的模板 。
    • static/目录:存放静态文件,如 CSS 样式表、JavaScript 脚本文件等 。
    • config/目录:专门用于存放配置文件,settings.py中可以定义各种配置类,如开发环境配置、生产环境配置等,通过在__init__.py中进行导入和选择,方便在不同环境下切换配置 。
    • migrations/目录:存放数据库迁移文件,用于管理数据库结构的版本控制,当数据模型发生变化时,可以通过数据库迁移工具(如 Flask - Migrate)生成迁移脚本,更新数据库结构 。
    • tests/目录:存放测试代码,对应用的各个模块进行单元测试和集成测试,确保代码的正确性和稳定性 。例如,test_routes.py中可以测试路由的功能是否正常,test_models.py中可以测试数据模型的方法和行为是否符合预期 。
    • requirements.txt文件:和前面的项目结构一样,记录项目依赖库 。
    • run.py文件:启动 Flask 应用 。

    通过这种大型项目结构设计,可以将复杂的应用分解为多个相对独立的模块,每个模块负责特定的功能,模块之间通过清晰的接口进行交互 。这不仅有利于团队成员分工协作开发,还能提高代码的可维护性、可扩展性和可测试性 。在实际项目中,可以根据项目的具体需求和特点,对这种结构进行适当的调整和优化 。

    九、总结与展望

    在这篇教程中,我们系统地学习了 Flask 框架的相关知识,从基础的环境搭建,到创建第一个简单的 Flask 应用,再深入探究其核心概念,如应用对象、路由系统、请求与响应处理等。我们还学习了如何使用 Jinja2 模板引擎实现动态页面渲染,通过表单处理与用户进行交互,利用 SQLAlchemy 进行数据库操作以实现数据的持久化存储,以及如何优化项目结构以适应不同规模的项目开发。

    Flask 框架凭借其轻量级和灵活性,在 Web 开发领域有着广泛的应用场景。通过本教程的学习,你已经具备了使用 Flask 开发小型 Web 应用的能力。然而,Flask 的世界远不止于此,它还有许多高级特性和丰富的扩展库等待你去探索,比如 Flask - RESTful 用于构建 RESTful API、Flask - SocketIO 实现实时通信功能、Flask - Migrate 进行数据库迁移管理等。

    希望你能在后续的学习和实践中,不断积累经验,将 Flask 应用到更多实际项目中,创造出功能丰富、用户体验良好的 Web 应用。如果你在学习过程中遇到问题或有任何心得,欢迎在评论区留言分享,让我们一起在 Flask 的学习道路上共同进步。

    英语之声