FastAPI 教程翻译 - 用户指南 25 - 依赖项

FastAPI 教程翻译 - 用户指南 25 - 依赖项

FastAPI Tutorial - User Guide - Dependencies

First Steps

第一步

FastAPI has a very powerful but intuitive Dependency Injection system.

FastAPI 具有非常强大而且直观的依赖注入系统。

It is designed to be very simple to use, and to make it very easy for any developer to integrate other components with FastAPI.

它的设计使用起来非常简单,并使任何开发人员都可以非常轻松地将其他组件与 FastAPI 集成在一起。

What is “Dependency Injection”

什么是『依赖注入』

"Dependency Injection" means, in programming, that there is a way for your code (in this case, your path operation functions) to declare things that it requires to work and use: “dependencies”.

『依赖注入』在编程中表示您的代码(在这种情况下,表示您的路径操作函数)有一种方法可以声明其需要工作和使用的东西:『依赖』。

And then, that system (in this case FastAPI) will take care of doing whatever is needed to provide your code with those needed dependencies (“inject” the dependencies).

然后,该系统(在本例中为 FastAPI)将负责完成为代码提供所需依赖项的所有操作(『注入』依赖项)。

This is very useful when you need to:

当您需要:

  • Have shared logic (the same code logic again and again).

    具有共享逻辑(一次又一次相同的代码逻辑)。

  • Share database connections.

    共享数据库连接。

  • Enforce security, authentication, role requirements, etc.

    强制执行安全性、身份验证、角色要求等。

  • And many other things…

    还有很多其他事情……

All these, while minimizing code repetition.

所有这些,同时最大限度地减少了代码重复。

First Steps

第一步

Let’s see a very simple example. It will be so simple that it is not very useful, for now.

让我们看一个非常简单的例子。它将是如此简单,以至于目前它还不是很有用。

But this way we can focus on how the Dependency Injection system works.

但是通过这种方式,我们可以专注于依赖注入系统的工作方式。

Create a dependency, or “dependable”
创建一个依赖项,或『被依赖项』

Let’s first focus on the dependency.

让我们首先关注依赖项。

It is just a function that can take all the same parameters that a path operation function can take:

它只是一个函数,可以采用路径操作函数可以采用的所有相同参数:

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

That’s it.

仅仅需要。

2 lines.

两行

And it has the same shape and structure that all your path operation functions.

它具有与所有路径操作函数相同的形状和结构。

You can think of it as a path operation function without the “decorator” (without the @app.get("/some-path")).

您可以将其视为没有『装饰器』(没有 @app.get("/some-path"))的路径操作函数

And it can return anything you want.

它可以返回您想要的任何东西。

In this case, this dependency expects:

在这种情况下,此依赖项期望如下:

  • An optional query parameter q that is a str.

    可选查询参数 qstr

  • An optional query parameter skip that is an int, and by default is 0.

    可选查询参数 skipint,默认值 0

  • An optional query parameter limit that is an int, and by default is 100.

    可选查询参数limitint,默认值 100

And then it just returns a dict containing those values.

然后,它仅返回包含这些值的 dict

Import Depends
导入 Depends
from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons
Declare the dependency, in the “dependant”
在『需依赖项』中声明依赖项

The same way you use Body, Query, etc. with your path operation function parameters, use Depends with a new parameter:

与将 BodyQuery 等与路径操作函数参数一起使用的方式相同,将 Depends 与新参数一起使用:

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

Although you use Depends in the parameters of your function the same way you use Body, Query, etc, Depends works a bit differently.

尽管您在函数的参数中使用 Depends 的方式与使用 BodyQuery 等的方式相同,但 Depends 的工作方式却有所不同。

You only give Depends a single parameter.

您只需给 Depends 一个参数。

This parameter must be something like a function.

此参数必须类似于函数。

And that function takes parameters in the same way that path operation functions do.

该函数采用与路径操作函数相同的方式获取参数。

Tip

提示

You’ll see what other “things”, apart from functions, can be used as dependencies in the next chapter.

在下一章中,您将看到除函数之外,还有哪些其他『事物』可用作依赖项。

Whenever a new request arrives, FastAPI will take care of:

每当有新请求到达时,FastAPI 就会处理:

  • Calling your dependency (“dependable”) function with the correct parameters.

    使用正确的参数调用依赖项(『被依赖项』)函数。

  • Get the result from your function.

    从函数中获取结果。

  • Assign that result to the parameter in your path operation function.

    将结果分配给路径操作函数中的参数。

在这里插入图片描述

This way you write shared code once and FastAPI takes care of calling it for your path operations.

这样,您只需编写一次共享代码,并且 FastAPI 可以为您的路径操作进行调用。

Check

检查

Notice that you don’t have to create a special class and pass it somewhere to FastAPI to “register” it or anything similar.

请注意,您不必创建特殊类并将其传递到 FastAPI 来『注册』它或类似的东西。

You just pass it to Depends and FastAPI knows how to do the rest.

您只需将其传递给 DependsFastAPI 就知道如何进行其余操作。

To async or not to async async

还是不async

As dependencies will also be called by FastAPI (the same as your path operation functions), the same rules apply while defining your functions.

由于依赖项还将由 FastAPI 调用(与路径操作函数相同),因此在定义函数时将应用相同的规则。

You can use async def or normal def.

您可以使用 async def 或者普通的 def

And you can declare dependencies with async def inside of normal def path operation functions, or def dependencies inside of async def path operation functions, etc.

您可以在普通的 def 路径操作函数内部使用 async def 声明依赖项,或者在 async def 路径操作函数内部使用 def 声明依赖,等等。

It doesn’t matter. FastAPI will know what to do.

没关系 FastAPI 会知道该怎么做。

Note

注意

If you don’t know, check the Async: “In a hurry?” section about async and await in the docs.

如果您不知道,请在文档中查看 Async: “In a hurry?” 部分,有关 asyncawait

Integrated with OpenAPI

与 OpenAPI 集成

All the request declarations, validations and requirements of your dependencies (and sub-dependencies) will be integrated in the same OpenAPI schema.

您的依赖项(和子依赖项)的所有请求声明、验证和要求都将集成在同一 OpenAPI 架构中。

So, the interactive docs will have all the information from these dependencies too:

因此,交互式文档也将从这些依赖项中获取所有信息:

在这里插入图片描述

Simple usage

简单用法

If you look at it, path operation functions are declared to be used whenever a path and operation matches, and then FastAPI takes care of calling the function with the correct parameters and use the response.

如果您看一看,只要路径操作匹配,就会声明使用路径操作函数,然后 FastAPI 会使用正确的参数来调用该函数并使用响应。

Actually, all (or most) of the web frameworks work in this same way.

实际上,所有(或大多数)Web框架都以相同的方式工作。

You never call those functions directly. They are called by your framework (in this case, FastAPI).

您永远不会直接调用这些函数。它们由您的框架调用(在本例中为 FastAPI)。

With the Dependency Injection system, you can also tell FastAPI that your path operation function also “depends” on something else that should be executed before your path operation function, and FastAPI will take care of executing it and “injecting” the results.

使用依赖注入系统,您还可以告诉 FastAPI,您的路径操作函数也『依赖』在您的路径操作函数之前应执行的其他操作,并且 FastAPI 会注意执行并『注入』结果中。

Other common terms for this same idea of “dependency injection” are:

相同的『依赖注入』概念的其他通用术语是:

  • resources

    资源

  • providers

    提供者

  • services

    服务

  • injectables

    可注入的

  • components

    组件

FastAPI plug-ins

FastAPI 插件

Integrations and "plug-in"s can be built using the Dependency Injection system. But in fact, there is actually no need to create “plug-ins”, as by using dependencies it’s possible to declare an infinite number of integrations and interactions that become available to your path operation functions.

可以使用依赖注入系统来构建集成和『插件』。但实际上,无需创建『插件』,因为通过使用依赖项,可以声明无限数量的集成和交互,这些集成和交互可用于路径操作函数

And dependencies can be created in a very simple and intuitive way that allow you to just import the Python packages you need, and integrate them with your API functions in a couple of lines of code, literally.

并且可以以非常简单直观的方式创建依赖项,从而使您可以导入所需的 Python 包,并将它们与 API 函数集成为几行代码,即字面意思

You will see examples of this in the next chapters, about relational and NoSQL databases, security, etc.

在下一章中,您将看到有关关系型数据库和 NoSQL 数据库、安全性等的示例。

FastAPI compatibility

FastAPI 兼容性

The simplicity of the dependency injection system makes FastAPI compatible with:

依赖注入系统的简单性使 **FastAPI **兼容如下:

  • all the relational databases

    所有关系型数据库

  • NoSQL databases

    NoSQL 数据库

  • external packages

    外部包

  • external APIs

    外部 API

  • authentication and authorization systems

    认证和授权系统

  • API usage monitoring systems

    API 使用情况监控系统

  • response data injection systems

    响应数据注入系统

  • etc.

    等等

Simple and Powerful

简单而强大

Although the hierarchical dependency injection system is very simple to define and use, it’s still very powerful.

尽管分层依赖注入系统的定义和使用非常简单,但它仍然非常强大。

You can define dependencies that in turn can define dependencies themselves.

您可以定义依赖项,而依赖项又可以自己定义依赖项。

In the end, a hierarchical tree of dependencies is built, and the Dependency Injection system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.

最后,构建了层次结构的依赖项树,依赖注入系统会为您解决所有这些依赖项(及其子依赖项),并在每个步骤中提供(注入)结果。

For example, let’s say you have 4 API endpoints (path operations):

例如,假设您有 4 个 API 端点(路径操作):

  • /items/public/
  • /items/private/
  • /users/{user_id}/activate
  • /items/pro/

then you could add different permission requirements for each of them just with dependencies and sub-dependencies:

那么您可以为每个依赖项和子依赖项添加不同的权限要求:

在这里插入图片描述

Integrated with OpenAPI

OpenAPI 集成

All these dependencies, while declaring their requirements, also add parameters, validations, etc. to your path operations.

所有这些依赖项在声明其要求的同时,还向路径操作中添加了参数、验证等。

FastAPI will take care of adding it all to the OpenAPI schema, so that it is shown in the interactive documentation systems.

FastAPI 将负责将其全部添加到 OpenAPI 架构中,以便在交互式文档系统中显示它。

Classes as Dependencies

类作为依赖项

Before diving deeper into the Dependency Injection system, let’s upgrade the previous example.

在深入研究依赖注入系统之前,让我们升级前面的示例。

A dict from the previous example

上例中的 dict

In the previous example, we were returning a dict from our dependency (“dependable”):

在前面的示例中,我们从依赖项(被依赖项)中返回一个 dict

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

But then we get a dict in the parameter commons of the path operation function.

然后我们在路径操作函数的参数 commons 中得到一个 dict

And we know that editors can’t provide a lot of support (like completion) for dicts, because they can’t know their keys and value types.

而且我们知道编辑器不能为 dict 提供很多支持(例如完整性检查),因为它们不知道其键和值类型。

We can do better…

我们可以做得更好……

What makes a dependency

是什么导致依赖

Up to now you have seen dependencies declared as functions.

到目前为止,您已经看到依赖项声明为函数。

But that’s not the only way to declare dependencies (although it would probably be the more common).

但这不是声明依赖项的唯一方法(尽管可能更常见)。

The key factor is that a dependency should be a “callable”.

关键因素是依赖项应该是『可调用的』。

A “callable” in Python is anything that Python can “call” like a function.

Python 中的『可调用的』是可以像函数一样『调用』的任何东西。

So, if you have an object something (that might not be a function) and you can “call” it (execute it) like:

因此,如果您有一个对象 something(可能不是一个函数),并且可以像下面这样『调用』它(执行):

something()

or

或者

something(some_argument, some_keyword_argument="foo")

then it is a “callable”.

那么它是一个『可调用的』。

Classes as dependencies

类作为依赖项

You might notice that to create an instance of a Python class, you use that same syntax.

您可能会注意到,使用相同的语法来创建 Python 类的实例。

For example:

例如:

class Cat:
    def __init__(self, name: str):
        self.name = name


fluffy = Cat(name="Mr Fluffy")

In this case, fluffy is an instance of the class Cat.

在这种情况下,fluffyCat 类的一个实例。

And to create fluffy, you are “calling” Cat.

要创建 fluffy,您就是『调用』Cat

So, a Python class is also a callable.

因此,Python类也是 可调用的

Then, in FastAPI, you could use a Python class as a dependency.

然后,在 FastAPI 中,您可以使用 Python 类作为依赖项。

What FastAPI actually checks is that it is a “callable” (function, class or anything else) and the parameters defined.

FastAPI 实际检查的是它是否为『可调用的』(函数、类或者其他任何东西),并且定义了参数。

If you pass a “callable” as a dependency in FastAPI, it will analyze the parameters for that “callable”, and process them in the same way as the parameters for a path operation function. Including sub-dependencies.

如果您在 FastAPI 中将『可调用的』作为依赖项传递,它将分析该『可调用的』参数,并以与路径操作函数的参数相同的方式处理它们。包括子依赖项。

That also applies to callables with no parameters at all. The same as it would be for path operation functions with no parameters.

这也适用于根本没有参数的可调用对象。与没有参数的路径操作函数相同。

Then, we can change the dependency “dependable” common_parameters from above to the class CommonQueryParameters:

然后,我们可以将依赖项『被依赖项』common_parameters 更改为类 CommonQueryParameters

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

Pay attention to the __init__ method used to create the instance of the class:

注意用于创建类实例的 __init__ 方法:

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

…it has the same parameters as our previous common_parameters:

…… 具有与我们先前的 common_parameters 相同的参数:

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

Those parameters are what FastAPI will use to “solve” the dependency.

这些参数是 FastAPI 用来『解决』依赖项的参数。

In both cases, it will have:

在这两种情况下,它都将具有:

  • an optional q query parameter.

    可选的 q 查询参数。

  • a skip query parameter, with a default of 0.

    一个 skip 查询参数,默认值为 0

  • a limit query parameter, with a default of 100.

    一个 limit 查询参数,默认值为 100

In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.

在这两种情况下,数据都将在 OpenAPI 架构上进行转换、验证和记录等。

Use it

使用它

Now you can declare your dependency using this class.

现在,您可以使用此类声明依赖项。

And as when FastAPI calls that class the value that will be passed as commons to your function will be an “instance” of the class, you can declare that parameter commons to be of type of the class, CommonQueryParams.

并且当 FastAPI 调用该类时,将作为 commons 传递给您的函数的值将成为该类的『实例』,因此您可以声明参数 commons 为该类的类型,CommonQueryParams

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

Type annotation vs Depends

类型注释与 Depends

In the code above, you are declaring commons as:

在上面的代码中,您将 commons 声明为:

commons: CommonQueryParams = Depends(CommonQueryParams)

The last CommonQueryParams, in:

最后一个 CommonQueryParams,位于:

... = Depends(CommonQueryParams)

…is what FastAPI will actually use to know what is the dependency.

…… 是 FastAPI 实际用于了解依赖项的内容。

From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call.

从那里开始,FastAPI 将提取声明的参数,而这正是 FastAPI 实际调用的参数。


In this case, the first CommonQueryParams, in:

在这种情况下,第一个 CommonQueryParams,位于:

commons: CommonQueryParams ...

…doesn’t have any special meaning for FastAPI. FastAPI won’t use it for data conversion, validation, etc. (as it is using the = Depends(CommonQueryParams) for that).

…… 对于 FastAPI 没有任何特殊含义。FastAPI 不会将其用于数据转换、验证等(因为它正在使用 = Depends(CommonQueryParams))。

You could actually write just:

您实际上可以只写:

commons = Depends(CommonQueryParams)

…as in:

…… 位于:

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons=Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

But declaring the type is encouraged as that way your editor will know what will be passed as the parameter commons, and then it can help you with code completion, type checks, etc:

但是鼓励声明类型,因为这样您的编辑器将知道作为参数 commons 传递的内容,然后它可以帮助您完成代码,进行类型检查等:

在这里插入图片描述

Shortcut

捷径

But you see that we are having some code repetition here, writing CommonQueryParams twice:

但是您会看到我们在这里有一些代码重复,写了两次 CommonQueryParams

commons: CommonQueryParams = Depends(CommonQueryParams)

FastAPI provides a shortcut for these cases, in where the dependency is specifically a class that FastAPI will “call” to create an instance of the class itself.

FastAPI 为这些情况提供了一种捷径,在这些情况下,特定的依赖项是 FastAPI 将『调用』以创建类本身实例的类。

For those specific cases, you can do the following:

对于这些特定情况,您可以执行以下操作:

Instead of writing:

而不是写:

commons: CommonQueryParams = Depends(CommonQueryParams)

…you write:

…… 您可以写:

commons: CommonQueryParams = Depends()

So, you can declare the dependency as the type of the variable, and use Depends() as the “default” value (the value after the =) for that function’s parameter, without any parameter, instead of having to write the full class again inside of Depends(CommonQueryParams).

因此,您可以将依赖项声明为变量的类型,并使用 Depends() 作为该函数的参数的『默认』值(=后的值),而无需任何参数,不必编写完整的类,并在 Depends(CommonQueryParams) 内部。

So, the same example would look like:

因此,相同的示例如下所示:

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends()):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

…and FastAPI will know what to do.

…… 并且 FastAPI 将知道该怎么做。

Tip

提示

If all that seems more confusing than helpful, disregard it, you don’t need it.

如果所有这些看起来比有用的都令人困惑,请无视它,您不需要它。

It is just a shortcut. Because FastAPI cares about helping you minimize code repetition.

这只是捷径。因为 FastAPI 希望帮助您最大程度地减少代码重复。

Sub-dependencies

子依赖项

You can create dependencies that have sub-dependencies.

您可以创建具有子依赖项的依赖项。

They can be as deep as you need them to be.

它们可以像您需要的一样

FastAPI will take care of solving them.

FastAPI 将负责解决它们。

First dependency “dependable”

第一个依赖项『被依赖项』

You could create a first dependency (“dependable”) like:

您可以创建第一个依赖项(『被依赖项』),例如:

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str = Cookie(None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

It declares an optional query parameter q as a str, and then it just returns it.

它声明一个可选的查询参数 qstr ,然后返回它。

This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.

这很简单(不是很有用),但是将帮助我们专注于子依赖项的工作方式。

Second dependency, “dependable” and “dependant”

第二个依赖项,『被依赖项』和『需依赖项』

Then you can create another dependency function (a “dependable”) that at the same time declares a dependency of its own (so it is a “dependant” too):

然后,您可以创建另一个依赖项函数(一个『被依赖项』),同时声明其自己的依赖项(因此它也是一个『需依赖项』):

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str = Cookie(None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

Let’s focus on the parameters declared:

让我们关注声明的参数:

  • Even though this function is a dependency (“dependable”) itself, it also declares another dependency (it “depends” on something else).

    即使此函数本身是一个依赖项(『被依赖项』),它也声明了另一个依赖项(它『依赖』其他东西)。

    • It depends on the query_extractor, and assigns the value returned by it to the parameter q.

      它取决于 query_extractor,并将其返回的值分配给参数 q

  • It also declares an optional last_query cookie, as a str.

    它还声明了一个可选的 last_query cookie,作为 str

    • Let’s imagine that if the user didn’t provide any query q, we just use the last query used, that we had saved to a cookie before.

      假设用户未提供任何查询 q,我们将使用上次使用的查询,该查询之前已保存到 Cookie 中。

Use the dependency

使用依赖项

Then we can use the dependency with:

然后,我们可以将依赖项用于:

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str = Cookie(None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

Info

信息

Notice that we are only declaring one dependency in the path operation function, the query_or_cookie_extractor.

请注意,我们仅在路径操作函数中声明了一个依赖项,即 query_or_cookie_extractor

But FastAPI will know that it has to solve query_extractor first, to pass the results of that to query_or_cookie_extractor while calling it.

但是,FastAPI 会知道它必须首先解决 query_extractor,然后在调用它时将结果传递给 query_or_cookie_extractor

在这里插入图片描述

Using the same dependency multiple times

多次使用相同的依赖项

If one of your dependencies is declared multiple times for the same path operation, for example, multiple dependencies have a common sub-dependency, FastAPI will know to call that sub-dependency only once per request.

例如,如果为同一个路径操作多次声明您的一个依赖项,则多个依赖项具有一个公共的子依赖项,FastAPI 将知道每个请求仅调用一次该子依赖项。

And it will save the returned value in a “cache” and pass it to all the “dependants” that need it in that specific request, instead of calling the dependency multiple times for the same request.

并且它将返回的值保存在『缓存』中,并将其传递给该特定请求中需要它的所有『需依赖项』,而不是针对同一请求多次调用依赖项。

In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the “cached” value, you can set the parameter use_cache=False when using Depends:

在高级场景中,您知道需要在同一请求中的每个步骤(可能多次)上调用依赖项,而不是使用『缓存值』,因此可以在使用 Depends 时设置参数 use_cache=False

async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
    return {"fresh_value": fresh_value}

Recap

回顾

Apart from all the fancy words used here, the Dependency Injection system is quite simple.

除了这里使用的所有花哨的单词以外,依赖注入系统非常简单。

Just functions that look the same as the path operation functions.

看起来与路径操作函数相同的函数。

But still, it is very powerful, and allows you to declare arbitrarily deeply nested dependency “graphs” (trees).

但是,它非常强大,并且允许您声明任意深度嵌套的依赖项『图形』(树)。

Tip

提示

All this might not seem as useful with these simple examples.

这些简单的示例似乎对所有这些都没有用。

But you will see how useful it is in the chapters about security.

但您会在有关安全性的章节中看到它的用处。

And you will also see the amounts of code it will save you.

并且您还看到它将为您节省的代码量。

Dependencies in path operation decorators

路径操作修饰符中的依赖项

In some cases you don’t really need the return value of a dependency inside your path operation function.

在某些情况下,您实际上不需要路径操作函数中的依赖项的返回值。

Or the dependency doesn’t return a value.

或依赖项不返回值。

But you still need it to be executed/solved.

但是您仍然需要执行/解决它。

For those cases, instead of declaring a path operation function parameter with Depends, you can add a list of dependencies to the path operation decorator.

在这种情况下,您可以在路径操作装饰器中添加 dependencieslist,而不是通过 Depends 声明路径操作函数参数。

Add dependencies to the path operation decorator

路径操作装饰器中添加 dependencies

The path operation decorator receives an optional argument dependencies.

路径操作装饰器接收一个可选的参数 dependencies

It should be a list of Depends():

它应该是 Depends()list

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


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


async def verify_key(x_key: str = Header(...)):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
    return [{"item": "Foo"}, {"item": "Bar"}]

These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won’t be passed to your path operation function.

这些依赖项将以与普通依赖项相同的方式执行/解决。但是,它们的值(如果它们返回任何值)将不会传递给您的路径操作函数

Tip

提示

Some editors check for unused function parameters, and show them as errors.

有些编辑器会检查未使用的功能参数,并将它们显示为错误。

Using these dependencies in the path operation decorator you can make sure they are executed while avoiding editor/tooling errors.

在路径操作装饰器中使用这些 dependencies,可以确保在避免编辑器/工具错误的情况下执行了它们。

It might also help avoiding confusion for new developers that see an un-used parameter in your code and could think it’s unnecessary.

对于那些在代码中看到未使用的参数并且认为不必要的新开发人员,这也可能有助于避免混淆。

Dependencies errors and return values

依赖项错误和返回值

You can use the same dependency functions you use normally.

您可以使用与通常使用的相同的依赖项功能

Dependency requirements
依赖性要求

They can declare request requirements (like headers) or other sub-dependencies:

他们可以声明请求要求(例如 headers)或其他子依赖项:

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


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


async def verify_key(x_key: str = Header(...)):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
    return [{"item": "Foo"}, {"item": "Bar"}]
Raise exceptions
触发异常

These dependencies can raise exceptions, the same as normal dependencies:

这些依赖可以触发异常,与普通的依赖项相同:

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


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


async def verify_key(x_key: str = Header(...)):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
    return [{"item": "Foo"}, {"item": "Bar"}]
Return values
返回值

And they can return values or not, the values won’t be used.

而且它们可以返回值或不返回值,将不会使用这些值。

So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won’t be used, the dependency will be executed:

因此,您可以重复使用已经在其他地方使用的普通依赖项(返回一个值),即使不使用该值,该依赖项也将被执行:

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


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


async def verify_key(x_key: str = Header(...)):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
    return [{"item": "Foo"}, {"item": "Bar"}]

Dependencies for a group of path operations

一组路径操作的依赖项

Later, when reading about how to structure bigger applications (Bigger Applications - Multiple Files), possibly with multiple files, you will learn how to declare a single dependencies parameter for a group of path operations.

稍后,当阅读有关如何构造可能包含多个文件的更大应用程序(更大的应用程序 - 多个文件)时,您将学习如何声明一个一组路径操作的单个 dependencies 参数。

Dependencies with yield

依赖项与yield

FastAPI supports dependencies that do some extra steps after finishing.

FastAPI 支持在完成后会执行一些额外步骤的依赖项。

To do this, use yield instead of return, and write the extra steps after.

为此,请使用 yield 而不是 return,然后再写一些额外的步骤。

Tip

提示

Make sure to use yield one single time.

确保单次使用 yield

Info

信息

For this to work, you need to use Python 3.7 or above, or in Python 3.6, install the “backports”:

为此,您需要使用 Python 3.7 或更高版本,或者在 Python 3.6 中,安装『backports』:

pip install async-exit-stack async-generator

This installs async-exit-stack and async-generator.

这会安装 async-exit-stackasync-generator

Technical Details

技术细节

Any function that is valid to use with:

任何可与以下功能一起使用的有效功能:

would be valid to use as a FastAPI dependency.

可以有效地用作 FastAPI 依赖项。

In fact, FastAPI uses those two decorators internally.

实际上,FastAPI 在内部使用了这两个装饰器。

A database dependency with yield

具有 yield 的数据库依赖项

For example, you could use this to create a database session and close it after finishing.

例如,您可以使用它来创建数据库会话并在完成后关闭它。

Only the code prior to and including the yield statement is executed before sending a response:

发送响应之前,仅执行 yield 语句之前的代码(包括该代码):

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

The yielded value is what is injected into path operations and other dependencies:

产生的值是注入到路径操作和其他依赖项中的值:

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

The code following the yield statement is executed after the response has been delivered:

传递响应后,将执行 yield 语句之后的代码:

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

Tip

提示

You can use async or normal functions.

您可以使用 async 或普通函数。

FastAPI will do the right thing with each, the same as with normal dependencies.

FastAPI 会对每种函数做正确的事情,与普通的依赖项相同。

A dependency with yield and try

依赖于 yieldtry

If you use a try block in a dependency with yield, you’ll receive any exception that was thrown when using the dependency.

如果在具有 yield 的依赖项中使用 try 块,则将收到使用依赖项时引发的任何异常。

For example, if some code at some point in the middle, in another dependency or in a path operation, made a database transaction “rollback” or create any other error, you will receive the exception in your dependency.

例如,如果某个代码在中间,其他依赖项或路径操作中的某个点上使数据库事务『回滚』或创建任何其他错误,则您将在依赖项中收到异常。

So, you can look for that specific exception inside the dependency with except SomeException.

因此,您可以使用 except SomeException 在依赖项中查找该特定异常。

In the same way, you can use finally to make sure the exit steps are executed, no matter if there was an exception or not.

以同样的方式,无论是否存在异常,您都可以使用 finally 来确保执行退出步骤。

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

Sub-dependencies with yield

具有 yield 的子依赖项

You can have sub-dependencies and “trees” of sub-dependencies of any size and shape, and any or all of them can use yield.

您可以拥有任何大小和形状的子依赖项的『树』,并且它们中的任何一个或全部都可以使用 yield

FastAPI will make sure that the “exit code” in each dependency with yield is run in the correct order.

FastAPI 将确保每个带有 yield 的依赖项中的『退出代码』以正确的顺序运行。

For example, dependency_c can have a dependency on dependency_b, and dependency_b on dependency_a:

例如,dependency_c 可以依赖于 dependency_b,而 dependency_b 可以依赖于 dependency_a

from fastapi import Depends


async def dependency_a():
    dep_a = generate_dep_a()
    try:
        yield dep_a
    finally:
        dep_a.close()


async def dependency_b(dep_a=Depends(dependency_a)):
    dep_b = generate_dep_b()
    try:
        yield dep_b
    finally:
        dep_b.close(dep_a)


async def dependency_c(dep_b=Depends(dependency_b)):
    dep_c = generate_dep_c()
    try:
        yield dep_c
    finally:
        dep_c.close(dep_b)

And all of them can use yield.

所有都可以使用 yield

In this case dependency_c, to execute its exit code, needs the value from dependency_b (here named dep_b) to still be available.

在这种情况下,dependency_c 要执行其退出代码,需要 dependency_b 中的值(此处称为 dep_b)仍然可用。

And, in turn, dependency_b needs the value from dependency_a (here named dep_a) to be available for its exit code.

并且,dependency_b 需要 dependency_a 中的值(在此称为 dep_a)可用于其退出代码。

from fastapi import Depends


async def dependency_a():
    dep_a = generate_dep_a()
    try:
        yield dep_a
    finally:
        dep_a.close()


async def dependency_b(dep_a=Depends(dependency_a)):
    dep_b = generate_dep_b()
    try:
        yield dep_b
    finally:
        dep_b.close(dep_a)


async def dependency_c(dep_b=Depends(dependency_b)):
    dep_c = generate_dep_c()
    try:
        yield dep_c
    finally:
        dep_c.close(dep_b)

The same way, you could have dependencies with yield and return mixed.

用同样的方式,您可能会混用 yieldreturn 的依赖项。

And you could have a single dependency that requires several other dependencies with yield, etc.

而且您可能只有一个依赖项,需要其他一些依赖项,例如 yield 等。

You can have any combinations of dependencies that you want.

您可以拥有所需的依赖项的任意组合。

FastAPI will make sure everything is run in the correct order.

FastAPI 将确保一切均以正确的顺序运行。

Technical Details

技术细节

This works thanks to Python’s Context Managers.

这要归功于Python的 Context Managers

FastAPI uses them internally to achieve this.

FastAPI 在内部使用它们来实现这一目标。

Dependencies with yield and HTTPException

具有 yieldHTTPException 的依赖项

You saw that you can use dependencies with yield and have try blocks that catch exceptions.

您已经看到可以将依赖项与 yield 结合使用,并具有 try 块来捕获异常。

It might be tempting to raise an HTTPException or similar in the exit code, after the yield. But it won’t work.

yield 之后,可能会在退出代码中触发 HTTPException 或类似的东西。但是它不会工作

The exit code in dependencies with yield is executed after Exception Handlers.

带有 yield 依赖项的退出代码是在 异常处理 之后执行的。

There’s nothing catching exceptions thrown by your dependencies in the exit code (after the yield).

在退出代码中(yield 之后),您的依赖项不会触发任何捕获异常。

So, if you raise an HTTPException after the yield, the default (or any custom) exception handler that catches HTTPExceptions and returns an HTTP 400 response won’t be there to catch that exception anymore.

因此,如果在 yield 之后触发 HTTPException,则捕获 HTTPException 并返回 HTTP 400 响应的默认(或任何自定义)异常处理程序将不再能够捕获该异常。

This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks.

这就是允许在依赖项(例如:数据库会话)中设置的任何内容供后台任务使用的方法。

Background tasks are run after the response has been sent. So there’s no way to raise an HTTPException because there’s not even a way to change the response that is already sent.

发送响应后,将在之后运行后台任务。因此,没有办法触发 HTTPException,因为甚至没有办法更改已经发送的响应。

But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with yield, and maybe log the error or report it to a remote tracking system.

但是,如果后台任务创建了一个数据库错误,至少您可以回退或完全清除带有 yield 的依赖项中的会话,并且可以记录该错误或将其报告给远程跟踪系统。

If you have some code that you know could raise an exception, do the most normal/“Pythonic” thing and add a try block in that section of the code.

如果您知道某些代码可能触发异常,请执行最正常 / 『Pythonic』的操作,并在该部分代码中添加一个 try 块。

If you have custom exceptions that you would like to handle before returning the response and possibly modifying the response, maybe even raising an HTTPException, create a Custom Exception Handler.

如果在返回响应并可能修改响应之前(如果有)想要处理自定义异常,甚至可能触发 HTTPException,请创建一个 自定义异常处理程序

Tip

提示

You can still raise exceptions including HTTPException before the yield. But not after.

您仍然可以在 yield 之前触发包括 HTTPException 在内的异常。而不是之后。

The sequence of execution is more or less like this diagram. Time flows from top to bottom. And each column is one of the parts interacting or executing code.

执行顺序或多或少类似于此图。时间从上到下流动。每列都是交互或执行代码的一部分。

在这里插入图片描述

在这里插入图片描述

Info

信息

Only one response will be sent to the client. It might be one of the error responses or it will be the response from the path operation.

一个响应将发送到客户端。它可能是错误响应之一,也可能是路径操作的响应。

After one of those responses is sent, no other response can be sent.

发送这些响应之一后,将无法再发送其他响应。

Tip

提示

This diagram shows HTTPException, but you could also raise any other exception for which you create a Custom Exception Handler. And that exception would be handled by that custom exception handler instead of the dependency exit code.

此图显示了 HTTPException,但是您也可以触发其他任何异常,以为其创建 自定义异常处理程序。而且该异常将由该自定义异常处理程序处理,而不是依赖项退出代码处理。

But if you raise an exception that is not handled by the exception handlers, it will be handled by the exit code of the dependency.

但是,如果触发了异常处理程序未处理的异常,则该异常将由依赖项的退出代码处理。

Context Managers

上下文管理器

What are “Context Managers”
什么是『上下文管理器』

“Context Managers” are any of those Python objects that you can use in a with statement.

『上下文管理器』是可以在 with 语句中使用的所有 Python 对象。

For example, you can use with to read a file:

例如,您可以使用 with 来读取文件

with open("./somefile.txt") as f:
    contents = f.read()
    print(contents)

Underneath, the open("./somefile.txt") creates an object that is a called a “Context Manager”.

在下面,open("./somefile.txt") 创建一个称为『上下文管理器』的对象。

When the with block finishes, it makes sure to close the file, even if there were exceptions.

with 块结束时,即使有异常,也要确保关闭文件。

When you create a dependency with yield, FastAPI will internally convert it to a context manager, and combine it with some other related tools.

当使用 yield 创建依赖项时,FastAPI 会将其内部转换为上下文管理器,并将其与其他一些相关工具组合。

Using context managers in dependencies with yield
在具有 yield 的依赖项中使用上下文管理器

Warning

警告

This is, more or less, an “advanced” idea.

这或多或少是一个『先进』的想法。

If you are just starting with FastAPI you might want to skip it for now.

如果您只是从 FastAPI 开始,那么您可能现在要跳过它。

In Python, you can create Context Managers by creating a class with two methods: __enter__() and __exit__().

在 Python 中,您可以通过 使用两种方法创建类:__enter__()__exit__() 来创建上下文管理器。

You can also use them inside of FastAPI dependencies with yield by using with or async with statements inside of the dependency function:

您还可以通过在依赖函数内部使用 withasync with 语句,在带有 yieldFastAPI 依赖项中使用它们:

class MySuperContextManager:
    def __init__(self):
        self.db = DBSession()

    def __enter__(self):
        return self.db

    def __exit__(self, exc_type, exc_value, traceback):
        self.db.close()


async def get_db():
    with MySuperContextManager() as db:
        yield db

Tip

提示

Another way to create a context manager is with:

创建上下文管理器的另一种方法是:

using them to decorate a function with a single yield.

使用它们来装饰具有一个 yield 的函数。

That’s what FastAPI uses internally for dependencies with yield.

这就是 FastAPI 在内部用于 yield 的依赖项。

But you don’t have to use the decorators for FastAPI dependencies (and you shouldn’t).

但是,您不必将装饰器用于 FastAPI 依赖项(也不应使用)。

FastAPI will do it for you internally.

FastAPI 将在内部为您完成。
ight want to skip it for now.

如果您只是从 FastAPI 开始,那么您可能现在要跳过它。

In Python, you can create Context Managers by creating a class with two methods: __enter__() and __exit__().

在 Python 中,您可以通过 使用两种方法创建类:__enter__()__exit__() 来创建上下文管理器。

You can also use them inside of FastAPI dependencies with yield by using with or async with statements inside of the dependency function:

您还可以通过在依赖函数内部使用 withasync with 语句,在带有 yieldFastAPI 依赖项中使用它们:

class MySuperContextManager:
    def __init__(self):
        self.db = DBSession()

    def __enter__(self):
        return self.db

    def __exit__(self, exc_type, exc_value, traceback):
        self.db.close()


async def get_db():
    with MySuperContextManager() as db:
        yield db

Tip

提示

Another way to create a context manager is with:

创建上下文管理器的另一种方法是:

using them to decorate a function with a single yield.

使用它们来装饰具有一个 yield 的函数。

That’s what FastAPI uses internally for dependencies with yield.

这就是 FastAPI 在内部用于 yield 的依赖项。

But you don’t have to use the decorators for FastAPI dependencies (and you shouldn’t).

但是,您不必将装饰器用于 FastAPI 依赖项(也不应使用)。

FastAPI will do it for you internally.

FastAPI 将在内部为您完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值