FastAPI 教程翻译 - 用户指南 30 - 更大的应用程序 - 多个文件

FastAPI 教程翻译 - 用户指南 30 - 更大的应用程序 - 多个文件

FastAPI Tutorial - User Guide - Bigger Applications - Multiple Files

If you are building an application or a web API, it’s rarely the case that you can put everything on a single file.

如果要构建应用程序或 Web API,则很少将所有内容都放在一个文件中。

FastAPI provides a convenience tool to structure your application while keeping all the flexibility.

FastAPI 提供了一种方便的工具,可在保持所有灵活性的同时构建应用程序。

Info

信息

If you come from Flask, this would be the equivalent of Flask’s Blueprints.

如果您来自 Flask,那将相当于 Flask 的蓝图。

An example file structure

文件结构示例

Let’s say you have a file structure like this:

假设您有一个像这样的文件结构:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── routers
│       ├── __init__.py
│       ├── items.py
│       └── users.py

Tip

提示

There are two __init__.py files: one in each directory or subdirectory.

有两个 __init__.py 文件:每个目录或子目录一个。

This is what allows importing code from one file into another.

这就是将代码从一个文件导入另一个文件的方法。

For example, in app/main.py you could have a line like:

例如,在 app/main.py 中,您可以有如下一行:

from app.routers import items
  • The app directory contains everything.

    app 目录包含所有内容。

  • This app directory has an empty file app/__init__.py.

    这个 app 目录有一个空文件 app/__init__.py

    • So, the app directory is a “Python package” (a collection of “Python modules”).

      因此,app 目录是『Python 包』(『Python 模块』的集合)。

  • The app directory also has a app/main.py file.

    app 目录中还有一个 app/main.py 文件。

    • As it is inside a Python package directory (because there’s a file __init__.py), it is a “module” of that package: app.main.

      由于它位于 Python 软件包目录中(因为存在文件 __init__.py),因此它是该软件包的『模块』:app.main

  • There’s a subdirectory app/routers/.

    有一个子目录 app/routers/

  • The subdirectory app/routers also has an empty file __init__.py.

    子目录 app/routers 中还有一个空文件 __init__.py

    • So, it is a “Python subpackage”.

      因此,它是一个『Python 子包』。

  • The file app/routers/items.py is beside the app/routers/__init__.py.

    文件 app/routers/items.py 位于 app/routers/__init__.py 旁边。

    • So, it’s a submodule: app.routers.items.

      因此,这是一个子模块:app.routers.items

  • The file app/routers/users.py is beside the app/routers/__init__.py.

    文件 app/routers/users.py 位于 app/routers/__init__.py 旁边。

    • So, it’s a submodule: app.routers.users.

      因此,它是一个子模块:app.routers.users

APIRouter

Let’s say the file dedicated to handling just users is the submodule at /app/routers/users.py.

假设专门用于处理用户的文件是 /app/routers/users.py 中的子模块。

You want to have the path operations related to your users separated from the rest of the code, to keep it organized.

您希望将与用户相关的路径操作与其余代码分开,以使其井井有条。

But it’s still part of the same FastAPI application/web API (it’s part of the same “Python Package”).

但它仍然是相同的 FastAPI 应用程序 / web API 的一部分(它是同一『Python 包』的一部分)。

You can create the path operations for that module using APIRouter.

您可以使用 APIRouter 为该模块创建路径操作

Import APIRouter

导入 APIRouter

You import it and create an “instance” the same way you would with the class FastAPI:

您可以导入它并以与 FastAPI 类相同的方式创建一个『实例』:

from fastapi import APIRouter

router = APIRouter()


@router.get("/users/", tags=["users"])
async def read_users():
    return [{"username": "Foo"}, {"username": "Bar"}]


@router.get("/users/me", tags=["users"])
async def read_user_me():
    return {"username": "fakecurrentuser"}


@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
    return {"username": username}

Path operations with APIRouter

使用 APIRouter 进行路径操作

And then you use it to declare your path operations.

然后,您可以使用它来声明路径操作

Use it the same way you would use the FastAPI class:

以与使用 FastAPI 类相同的方式使用它:

from fastapi import APIRouter

router = APIRouter()


@router.get("/users/", tags=["users"])
async def read_users():
    return [{"username": "Foo"}, {"username": "Bar"}]


@router.get("/users/me", tags=["users"])
async def read_user_me():
    return {"username": "fakecurrentuser"}


@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
    return {"username": username}

You can think of APIRouter as a "mini FastAPI" class.

您可以将 APIRouter 视为『小型 FastAPI』类。

All the same options are supported.

支持所有相同的选项。

All the same parameters, responses, dependencies, tags, etc.

所有相同的参数、响应、依赖性、标签等。

Tip

提示

In this example, the variable is called router, but you can name it however you want.

在此示例中,该变量称为 router,但您可以根据需要命名。

We are going to include this APIrouter in the main FastAPI app, but first, let’s add another APIRouter.

我们将在主 FastAPI 应用中包含该 APIrouter,但首先,让我们添加另一个 APIRouter

Another module with APIRouter

另一个带有 APIRouter 的模块

Let’s say you also have the endpoints dedicated to handling “Items” from your application in the module at app/routers/items.py.

假设您在模块 app/routers/items.py 中还具有专用于处理应用程序中『项目』的端点。

You have path operations for:

您具有路径操作

  • /items/
  • /items/{item_id}

It’s all the same structure as with app/routers/users.py.

app/routers/users.py 的结构相同。

But let’s say that this time we are more lazy.

但是,可以说这一次我们比较懒。

And we don’t want to have to explicitly type /items/ and tags=["items"] in every path operation (we will be able to do it later):

而且,我们不需要在每个路径操作中显式地键入 /items/tags=["items"](稍后我们将可以这样做):

from fastapi import APIRouter, HTTPException

router = APIRouter()


@router.get("/")
async def read_items():
    return [{"name": "Item Foo"}, {"name": "item Bar"}]


@router.get("/{item_id}")
async def read_item(item_id: str):
    return {"name": "Fake Specific Item", "item_id": item_id}


@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "foo":
        raise HTTPException(status_code=403, detail="You can only update the item: foo")
    return {"item_id": item_id, "name": "The Fighters"}

Add some custom tags, responses, and dependencies

添加一些自定义的 tagsresponsesdependencies

We are not adding the prefix /items/ nor the tags=["items"] to add them later.

我们没有添加前缀 /items/tags=["items"] 以便以后添加它们。

But we can add custom tags and responses that will be applied to a specific path operation:

但是我们可以添加自定义的 tagsresponses ,这些标签将应用于特定的路径操作

from fastapi import APIRouter, HTTPException

router = APIRouter()


@router.get("/")
async def read_items():
    return [{"name": "Item Foo"}, {"name": "item Bar"}]


@router.get("/{item_id}")
async def read_item(item_id: str):
    return {"name": "Fake Specific Item", "item_id": item_id}


@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "foo":
        raise HTTPException(status_code=403, detail="You can only update the item: foo")
    return {"item_id": item_id, "name": "The Fighters"}

The main FastAPI

主要的FastAPI

Now, let’s see the module at app/main.py.

现在,让我们在 app/main.py 处查看该模块。

Here’s where you import and use the class FastAPI.

在这里导入并使用 FastAPI 类。

This will be the main file in your application that ties everything together.

这将是应用程序中将所有内容捆绑在一起的主文件。

Import FastAPI

导入 FastAPI

You import and create a FastAPI class as normally:

您可以像往常一样导入并创建一个 FastAPI 类:

from fastapi import Depends, FastAPI, Header, HTTPException

from .routers import items, users

app = FastAPI()


async def get_token_header(x_token: str = Header(...)):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


app.include_router(users.router)
app.include_router(
    items.router,
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

Import the APIRouter

导入 APIRouter

But this time we are not adding path operations directly with the FastAPI app.

但这一次我们不是直接在 FastAPI app 中添加路径操作

We import the other submodules that have APIRouters:

我们导入其他具有 APIRouters 的子模块:

from fastapi import Depends, FastAPI, Header, HTTPException

from .routers import items, users

app = FastAPI()


async def get_token_header(x_token: str = Header(...)):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


app.include_router(users.router)
app.include_router(
    items.router,
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

As the file app/routers/items.py is part of the same Python package, we can import it using “dot notation”.

由于文件 app/routers/items.py 是同一 Python 包的一部分,因此我们可以使用『点符号』将其导入。

How the importing works

导入方式

The section:

这部分:

from .routers import items, users

Means:

意味着:

  • Starting in the same package that this module (the file app/main.py) lives in (the directory app/)…

    从与此模块(文件 app/main.py)所在的相同包(目录 app/)开始……

  • look for the subpackage routers (the directory at app/routers/)…

    寻找子包 routers(目录位于 app/routers/)……

  • and from it, import the submodule items (the file at app/routers/items.py) and users (the file at app/routers/users.py)…

    然后从其中导入子模块 items(位于 app/routers/items.py 的文件)和 users(位于 app/routers/users.py 的文件)……

The module items will have a variable router (items.router). This is the same one we created in the file app/routers/items.py. It’s an APIRouter. The same for the module users.

模块 items 将具有变量 routeritems.router)。这与我们在文件 app/routers/items.py 中创建的相同。这是一个 APIRouter。用户模块也一样。

We could also import them like:

我们也可以像这样导入它们:

from app.routers import items, users

Info

信息

The first version is a “relative import”.

第一个版本是『相对导入』。

The second version is an “absolute import”.

第二个版本是『绝对导入』。

To learn more about Python Packages and Modules, read the official Python documentation about Modules.

要了解有关 Python 软件包和模块的更多信息,请阅读 有关模块的 Python 官方文档

Avoid name collisions

避免名称冲突

We are importing the submodule items directly, instead of importing just its variable router.

我们将直接导入子模块 items,而不是仅导入其变量 router

This is because we also have another variable named router in the submodule users.

这是因为我们在子模块 users 中还有另一个名为 router 的变量。

If we had imported one after the other, like:

如果我们一个接一个地导入,例如:

from .routers.items import router
from .routers.users import router

The router from users would overwrite the one from items and we wouldn’t be able to use them at the same time.

来自 usersrouter 将覆盖 items 中的 router,我们将无法同时使用它们。

So, to be able to use both of them in the same file, we import the submodules directly:

因此,为了能够在同一个文件中使用它们,我们直接导入子模块:

from fastapi import Depends, FastAPI, Header, HTTPException

from .routers import items, users

app = FastAPI()


async def get_token_header(x_token: str = Header(...)):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


app.include_router(users.router)
app.include_router(
    items.router,
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

Include an APIRouter

包含一个 APIRouter

Now, let’s include the router from the submodule users:

现在,让我们从子模块 users 中包含 router

from fastapi import Depends, FastAPI, Header, HTTPException

from .routers import items, users

app = FastAPI()


async def get_token_header(x_token: str = Header(...)):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


app.include_router(users.router)
app.include_router(
    items.router,
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

Info

信息

users.router contains the APIRouter inside of the file app/routers/users.py.

users.router 在文件 app/routers/users.py 中包含 APIRouter

With app.include_router() we can add an APIRouter to the main FastAPI application.

通过 app.include_router(),我们可以将 APIRouter 添加到主 FastAPI 应用中。

It will include all the routes from that router as part of it.

它将包括来自该路由器的所有路由作为其一部分。

Technical Details

技术细节

It will actually internally create a path operation for each path operation that was declared in the APIRouter.

实际上,它将为内部在 APIRouter 中声明的每个路径操作创建一个路径操作

So, behind the scenes, it will actually work as if everything was the same single app.

因此,在幕后,它实际上像一切都是同一个应用程序一样工作。

Check

检查

You don’t have to worry about performance when including routers.

包括路由器时,您不必担心性能。

This will take microseconds and will only happen at startup.

这将需要几微秒,并且只会在启动时发生。

So it won’t affect performance.

因此,它不会影响性能。

Include an APIRouter with a prefix, tags, responses, and dependencies

包括带有 prefixtagsresponsesdependenciesAPIRouter

Now, let’s include the router from the items submodule.

现在,让我们包括来自 items 子模块的路由器。

But, remember that we were lazy and didn’t add /items/ nor tags to all the path operations?

但是,请记住我们很懒,没有为所有路径操作添加 /items/tags 吗?

We can add a prefix to all the path operations using the parameter prefix of app.include_router().

我们可以使用 app.include_router() 的参数 prefix 为所有路径操作添加前缀。

As the path of each path operation has to start with /, like in:

由于每个路径操作的路径都必须以 / 开头,例如:

@router.get("/{item_id}")
async def read_item(item_id: str):
    ...

…the prefix must not include a final /.

…… 前缀不得包含最后的 /

So, the prefix in this case would be /items.

因此,这种情况下的前缀为 /items

We can also add a list of tags that will be applied to all the path operations included in this router.

我们还可以添加一个 tags 列表,该列表将应用于此路由器中包括的所有路径操作

And we can add predefined responses that will be included in all the path operations too.

我们还可以添加预定义的 responses,这些响应也将包含在所有路径操作中。

And we can add a list of dependencies that will be added to all the path operations in the router and will be executed/solved for each request made to them. Note that, much like dependencies in path operation decorators, no value will be passed to your path operation function.

我们可以添加一个 dependencies 列表,该列表将被添加到路由器中的所有路径操作中,并将针对对它们的每个请求执行 / 解决。请注意,就像路径操作装饰器中的依赖项一样,任何值都不会传递给路径操作函数

from fastapi import Depends, FastAPI, Header, HTTPException

from .routers import items, users

app = FastAPI()


async def get_token_header(x_token: str = Header(...)):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


app.include_router(users.router)
app.include_router(
    items.router,
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

The end result is that the item paths are now:

最终结果是项目路径现在为:

  • /items/
  • /items/{item_id}

…as we intended.

…… 如我们所愿。

  • They will be marked with a list of tags that contain a single string "items".

    它们将被标记为包含单个字符串『items』的标签列表。

  • The path operation that declared a "custom" tag will have both tags, items and custom.

    声明了『custom』标签的路径操作将同时具有 itemscustom 两个标签。

    • These “tags” are especially useful for the automatic interactive documentation systems (using OpenAPI).

      这些『标签』对于自动交互式文档系统(使用 OpenAPI)特别有用。

  • All of them will include the predefined responses.

    所有这些都将包含预定义的 responses

  • The path operation that declared a custom 403 response will have both the predefined responses (404) and the 403 declared in it directly.

    声明了自定义 403 响应的路径操作将同时具有预定义的响应(404)和直接声明的 403

  • All these path operations will have the list of dependencies evaluated/executed before them.

    所有这些路径操作将在其之前评估 / 执行 dependencies 列表。

Tip

提示

Having dependencies in a decorator can be used, for example, to require authentication for a whole group of path operations. Even if the dependencies are not added individually to each one of them.

例如,可以使用在修饰器中具有 dependencies 来要求对整个路径操作组进行身份验证。即使没有将依赖项单独添加到每个依赖项中。

Check

检查

The prefix, tags, responses and dependencies parameters are (as in many other cases) just a feature from FastAPI to help you avoid code duplication.

prefixtagsresponsesdependencies 参数(在许多其他情况下)只是 FastAPI 中的一项功能,可帮助您避免代码重复。

Tip

提示

You could also add path operations directly, for example with: @app.get(...).

您也可以直接添加路径操作,例如:@app.get(...)

Apart from app.include_router(), in the same FastAPI app.

在同一 FastAPI 应用中,除了 app.include_router()

It would still work the same.

仍然可以使用。

Very Technical Details

深度技术细节

Note: this is a very technical detail that you probably can just skip.

注意:这是一个深度技术细节,您可能可以跳过它。


The APIRouters are not “mounted”, they are not isolated from the rest of the application.

APIRouter 没有被『挂载』,它们与应用程序的其余部分没有隔离。

This is because we want to include their path operations in the OpenAPI schema and the user interfaces.

这是因为我们要在 OpenAPI 架构和用户界面中包括它们的路径操作

As we cannot just isolate them and “mount” them independently of the rest, the path operations are “cloned” (re-created), not included directly.

由于我们不能仅将它们隔离并独立于其余部分来『挂载』,因此路径操作是『克隆的』(重新创建),而不是直接包含在内。

Check the automatic API docs

检查自动 API 文档

Now, run uvicorn, using the module app.main and the variable app:

现在,使用模块 app.main 和变量 app ,运行 uvicorn

uvicorn app.main:app --reload

And open the docs at http://127.0.0.1:8000/docs.

并在 http://127.0.0.1:8000/docs 中打开文档。

You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags:

您将看到使用正确的路径(和前缀)和正确的标签的自动 API 文档,包括所有子模块的路径:

在这里插入图片描述

Include the same router multiple times with different prefix

多次使用不同的 prefix 添加同一路由器

You can also use .include_router() multiple times with the same router using different prefixes.

您还可以给同一路由器使用不同的前缀多次使用 .include_router()

This could be useful, for example, to expose the same API under different prefixes, e.g. /api/v1 and /api/latest.

例如,在不同的前缀下公开相同的 API,/api/v1/api/latest

This is an advanced usage that you might not really need, but it’s there in case you do.

.0.1:8000/docs 中打开文档。

You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags:

您将看到使用正确的路径(和前缀)和正确的标签的自动 API 文档,包括所有子模块的路径:

[外链图片转存中…(img-4F6ZjDe3-1591541294584)]

Include the same router multiple times with different prefix

多次使用不同的 prefix 添加同一路由器

You can also use .include_router() multiple times with the same router using different prefixes.

您还可以给同一路由器使用不同的前缀多次使用 .include_router()

This could be useful, for example, to expose the same API under different prefixes, e.g. /api/v1 and /api/latest.

例如,在不同的前缀下公开相同的 API,/api/v1/api/latest

This is an advanced usage that you might not really need, but it’s there in case you do.

这是您可能真正不需要的高级用法,但是如果您有需要,可以使用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值