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

之前例子中的dict

在前面的示例中,我们从依赖项(“ dependable”)中返回了一个字典:

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
  4. return {"q": q, "skip": skip, "limit": limit}
  5. @app.get("/items/")
  6. async def read_items(commons: dict = Depends(common_parameters)):
  7. return commons
  8. @app.get("/users/")
  9. async def read_users(commons: dict = Depends(common_parameters)):
  10. return commons

但是,然后我们得到了从路径操作函数得到了的一个带有参数common变量dict
而且我们知道编辑器无法为字典提供很多支持(例如完成),因为他们不知道其键和值类型。我们可以做得更好…

是什么导致了依赖

到现在为止,您已经看到依赖项声明为函数。
但这不是声明依赖关系的唯一方法(尽管可能更常见)。
关键因素是依赖项应该是“可调用的”。
Python中的“可调用”是Python可以像函数一样“调用”的任何东西。
因此,如果您有一个对象(可能不是函数),则可以像下面这样“调用”(执行)该对象:

  1. something()

或者

  1. something(some_argument, some_keyword_argument="foo")

那么它是一个“可调用的”。

类作为依赖

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

  1. class Cat:
  2. def __init__(self, name: str):
  3. self.name = name
  4. fluffy = Cat(name="Mr Fluffy")

在这种情况下,fluffy是Cat类的实例。
为了创建fluffy,您是在“调用”Cat
因此,Python类也是可调用的。
然后,在FastAPI中,您可以使用Python类作为依赖项。
FastAPI实际检查的是它是“可调用的”(函数,类或其他任何东西),并且已定义参数。
如果在FastAPI中将“可调用”作为依赖项传递,它将分析该“可调用”的参数,并以与路径操作函数的参数相同的方式处理它们。包括子依赖项。
这也适用于根本没有参数的可调用对象。与没有参数的路径操作功能相同。
然后,我们可以将依赖项“dependable” common_parameters从上面更改为类CommonQueryParameters

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
  4. class CommonQueryParams:
  5. def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
  6. self.q = q
  7. self.skip = skip
  8. self.limit = limit
  9. @app.get("/items/")
  10. async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
  11. response = {}
  12. if commons.q:
  13. response.update({"q": commons.q})
  14. items = fake_items_db[commons.skip : commons.skip + commons.limit]
  15. response.update({"items": items})
  16. return response

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

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
  4. class CommonQueryParams:
  5. def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
  6. self.q = q
  7. self.skip = skip
  8. self.limit = limit
  9. @app.get("/items/")
  10. async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
  11. response = {}
  12. if commons.q:
  13. response.update({"q": commons.q})
  14. items = fake_items_db[commons.skip : commons.skip + commons.limit]
  15. response.update({"items": items})
  16. return response

…其参数与之前的common_parameters相同:

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
  4. return {"q": q, "skip": skip, "limit": limit}
  5. @app.get("/items/")
  6. async def read_items(commons: dict = Depends(common_parameters)):
  7. return commons
  8. @app.get("/users/")
  9. async def read_users(commons: dict = Depends(common_parameters)):
  10. return commons

这些参数是FastAPI将用于“解决”依赖关系的参数。
在这两种情况下,它都将具有:

  • 可选的q查询参数。
  • skip查询参数,默认值为0。
  • limit查询参数,默认值为100。

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

使用它

现在,您可以使用此类声明依赖项。
并且当FastAPI调用该类时,将作为commons对象传递给您的函数的值将是该类的“实例”,因此您可以将参数commons对象声明为该类的类型CommonQueryParams

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
  4. class CommonQueryParams:
  5. def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
  6. self.q = q
  7. self.skip = skip
  8. self.limit = limit
  9. @app.get("/items/")
  10. async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
  11. response = {}
  12. if commons.q:
  13. response.update({"q": commons.q})
  14. items = fake_items_db[commons.skip : commons.skip + commons.limit]
  15. response.update({"items": items})
  16. return response

类型注释与Depends

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

  1. commons: CommonQueryParams = Depends(CommonQueryParams)

最后一个CommonQueryParams,位于:

  1. ... = Depends(CommonQueryParams)

…是FastAPI实际用来了解依赖项的内容。
从这里开始,FastAPI将提取声明的参数,而这正是FastAPI实际调用的参数。


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

  1. commons: CommonQueryParams ...

…对于FastAPI没有任何特殊含义。 FastAPI不会将其用于数据转换,验证等(因为它使用的是= Depends(CommonQueryParams))。
您实际上可以只写:

  1. commons = Depends(CommonQueryParams)

在:

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
  4. class CommonQueryParams:
  5. def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
  6. self.q = q
  7. self.skip = skip
  8. self.limit = limit
  9. @app.get("/items/")
  10. async def read_items(commons=Depends(CommonQueryParams)):
  11. response = {}
  12. if commons.q:
  13. response.update({"q": commons.q})
  14. items = fake_items_db[commons.skip : commons.skip + commons.limit]
  15. response.update({"items": items})
  16. return response

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

捷径

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

  1. commons: CommonQueryParams = Depends(CommonQueryParams)

FastAPI为这些情况提供了一种快捷方式,其中依赖项是FastAPI将“调用”以创建该类本身的实例的类。
对于这些特定情况,您可以执行以下操作:
无需编写:

  1. commons: CommonQueryParams = Depends(CommonQueryParams)

你可以直接写:

  1. commons: CommonQueryParams = Depends()

因此,您可以将依赖项声明为变量的类型,并使用Depends()作为该函数参数的“默认”值(=后面的值),而无需任何参数,而不必再次编写完整的类在Depends(CommonQueryParams)内部。
因此,相同的示例如下所示:

  1. from fastapi import Depends, FastAPI
  2. app = FastAPI()
  3. fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
  4. class CommonQueryParams:
  5. def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
  6. self.q = q
  7. self.skip = skip
  8. self.limit = limit
  9. @app.get("/items/")
  10. async def read_items(commons: CommonQueryParams = Depends()):
  11. response = {}
  12. if commons.q:
  13. response.update({"q": commons.q})
  14. items = fake_items_db[commons.skip : commons.skip + commons.limit]
  15. response.update({"items": items})
  16. return response

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

:::tips 这只是捷径。因为FastAPI关心帮助您最大程度地减少代码重复。 :::