你不需要一个框架。你需要的是一幅画,而不是画框。
——Klaus Kinski,演员
预览
对于使用过 Flask、Django 或其他流行 Python Web 框架的开发者来说,本章会指出 FastAPI 与它们的相似点和差异。本章不会深入每一个令人痛苦的细节,否则这本书的装订胶都撑不住了。如果你正在考虑把某个应用程序从这些框架之一迁移到 FastAPI,或者只是感到好奇,本章的比较会很有用。
关于一个新的 Web 框架,你最先想知道的事情之一,可能就是如何开始使用它。自顶向下的一种方式,是定义路由,也就是从 URL 和 HTTP 方法到函数的映射。下一节会比较如何用 FastAPI 和 Flask 来做这件事,因为它们彼此之间比 Django 更相似,也更可能在类似应用程序中被一起考虑。
Flask
Flask 称自己为微框架。它提供基础能力,你可以根据需要下载第三方包来补充它。它比 Django 更小,刚开始学习时也更快上手。
Flask 是同步的,基于 WSGI,而不是 ASGI。一个名为 quart 的新项目正在复制 Flask,并添加 ASGI 支持。
让我们从顶层开始,展示 Flask 和 FastAPI 如何定义 Web 路由。
路径
在顶层,Flask 和 FastAPI 都使用装饰器来把路由与 Web 端点关联起来。在示例 7-1 中,让我们复制第 3 章中的示例 3-11,也就是从 URL 路径中获取要问候的人。
示例 7-1 FastAPI 路径
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi/{who}")
def greet(who: str):
return f"Hello? {who}?"
默认情况下,FastAPI 会把那个 f"Hello? {who}?" 字符串转换为 JSON,并将其返回给 Web 客户端。
示例 7-2 展示了 Flask 会如何实现。
示例 7-2 Flask 路径
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/hi/<who>", methods=["GET"])
def greet(who: str):
return jsonify(f"Hello? {who}?")
注意,装饰器中的 who 现在被 < 和 > 包围。在 Flask 中,方法需要作为参数包含进去——除非它是默认值 GET。所以这里的 methods=["GET"] 本来可以省略,但显式写出来从来不会有坏处。
注意
Flask 2.0 支持 FastAPI 风格的装饰器,比如 @app.get,而不是 app.route。
Flask 的 jsonify() 函数会把它的参数转换成 JSON 字符串并返回,同时带上表示这是 JSON 的 HTTP 响应头。如果你返回的是一个 dict,而不是其他数据类型,较新版本的 Flask 会自动把它转换成 JSON 并返回。显式调用 jsonify() 对所有数据类型都有效,包括 dict。
查询参数
在示例 7-3 中,让我们重复示例 3-15,其中 who 是作为查询参数传入的,也就是 URL 中 ? 之后的部分。
示例 7-3 FastAPI 查询参数
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet(who):
return f"Hello? {who}?"
Flask 的等价版本如示例 7-4 所示。
示例 7-4 Flask 查询参数
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/hi", methods=["GET"])
def greet():
who: str = request.args.get("who")
return jsonify(f"Hello? {who}?")
在 Flask 中,我们需要从 request 对象中获取请求值。在这个例子中,args 是一个包含查询参数的字典。
请求体
在示例 7-5 中,让我们复制旧的示例 3-21。
示例 7-5 FastAPI 请求体
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet(who):
return f"Hello? {who}?"
Flask 版本如示例 7-6 所示。
示例 7-6 Flask 请求体
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/hi", methods=["GET"])
def greet():
who: str = request.json["who"]
return jsonify(f"Hello? {who}?")
Flask 会把 JSON 输入存储在 request.json 中。
请求头
最后,让我们在示例 7-7 中重复示例 3-24。
示例 7-7 FastAPI 请求头
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/hi")
def greet(who:str = Header()):
return f"Hello? {who}?"
Flask 版本如示例 7-8 所示。
示例 7-8 Flask 请求头
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/hi", methods=["GET"])
def greet():
who: str = request.headers.get("who")
return jsonify(f"Hello? {who}?")
和查询参数一样,Flask 会把请求数据保存在 request 对象中。这一次,它位于 headers 这个字典属性里。请求头的键本应是不区分大小写的。
Django
Django 比 Flask 或 FastAPI 更大,也更复杂。根据它的网站说法,它面向的是“有截止日期的完美主义者”。它内置的对象关系映射器,也就是 ORM,对于拥有大型数据库后端的网站很有用。相比工具包,它更像是一个单体框架。额外的复杂性和学习曲线是否值得,取决于你的应用程序。
虽然 Django 传统上是一个 WSGI 应用程序,但 3.0 版本增加了对 ASGI 的支持。
与 Flask 和 FastAPI 不同,Django 喜欢在一个单独的 URLConf 表中定义路由,也就是把 URL 与 Web 函数关联起来;Django 把这些 Web 函数称为视图函数,而不是使用装饰器。这让你更容易在一个地方看到所有路由,但当你只看某个函数时,也更难看出哪个 URL 与它关联。
其他 Web 框架特性
在前面比较这三个框架的几节中,我主要比较了如何定义路由。一个 Web 框架通常也可能被期望在以下其他方面提供帮助:
表单
这三个包都支持标准 HTML 表单。
文件
这些包都能处理文件上传和下载,包括 multipart HTTP 请求和响应。
模板
模板语言允许你混合文本和代码。它适合面向内容的网站,也就是带有动态插入数据的 HTML 文本,而不是 API 网站。最知名的 Python 模板包是 Jinja,Flask、Django 和 FastAPI 都支持它。Django 也有自己的模板语言。
如果你想使用基础 HTTP 之外的网络方法,可以尝试这些:
服务器发送事件
按需向客户端推送数据。FastAPI 通过 sse-starlette 支持,Flask 通过 Flask-SSE 支持,Django 通过 Django EventStream 支持。
队列
任务队列、发布-订阅以及其他网络模式,由 ZeroMQ、Celery、Redis 和 RabbitMQ 等外部包支持。
WebSockets
FastAPI 直接支持,Django 通过 Django Channels 支持,Flask 通过第三方包支持。
数据库
Flask 和 FastAPI 的基础包中不包含任何数据库处理能力,但数据库处理是 Django 的一个关键特性。
你的网站 Data 层可能在不同层级访问数据库:
直接 SQL,例如 PostgreSQL、SQLite
直接 NoSQL,例如 Redis、MongoDB、Elasticsearch
生成 SQL 的 ORM
生成 NoSQL 的对象文档/数据映射器/管理器,也就是 ODM
对于关系型数据库,SQLAlchemy 是一个出色的包,它包含多个访问层级,从直接 SQL 到 ORM。对于 Flask 和 FastAPI 开发者来说,这是一个常见选择。FastAPI 的作者同时利用了 SQLAlchemy 和 Pydantic,开发了 SQLModel 包,第 14 章会进一步讨论它。
对于数据库需求很重的网站,Django 通常是框架选择。它有自己的 ORM 和一个自动化数据库管理页面。虽然有些资料建议让非技术人员使用这个管理页面进行日常数据管理,但要小心。在一个案例中,我见过一位非专家误解了管理页面的警告消息,结果导致数据库需要从备份中手动恢复。
第 14 章会更深入地讨论 FastAPI 和数据库。
推荐建议
对于基于 API 的服务来说,FastAPI 现在似乎是最佳选择。在快速启动并运行一个服务方面,Flask 和 FastAPI 大致相当。Django 需要更多时间来理解,但它为更大型的网站提供了许多有用功能,尤其是那些严重依赖数据库的网站。
其他 Python Web 框架
当前 Python Web 框架的三巨头是 Flask、Django 和 FastAPI。用 Google 搜索 python web frameworks,你会得到大量建议,我就不在这里重复了。有几个框架可能在那些列表中并不突出,但出于这样或那样的原因比较有趣,包括:
Bottle
一个非常极简的包,只有单个 Python 文件,适合快速做概念验证。
Litestar
类似 FastAPI——它基于 ASGI/Starlette 和 Pydantic——但有自己的主张。
AIOHTTP
一个 ASGI 客户端和服务器,带有有用的演示代码。
Socketify.py
一个新进入者,声称具备非常高的性能。
回顾
Flask 和 Django 是最流行的 Python Web 框架,尽管 FastAPI 的流行度增长更快。这三者都能处理基本的 Web 服务器任务,但学习曲线各不相同。FastAPI 在指定路由方面似乎拥有更简洁的语法,并且它对 ASGI 的支持,使它在许多情况下能够比竞争对手运行得更快。接下来:我们终于来构建一个网站吧。