mirror of
https://github.com/fastapi/fastapi.git
synced 2026-10-27 04:09:37 -04:00
📝 Update and add docs for dependencies
This commit is contained in:
1 parent
332ee4aee1
commit
b79c13baed
11 files changed
+390
-152
No files matched your search
@@ -0,0 +1,71 @@
|
||||
!!! danger
|
||||
This is, more or less, an "advanced" chapter.
|
||||
|
||||
If you are just starting with **FastAPI** you might want to skip this chapter and come back to it later.
|
||||
|
||||
## Parameterized dependencies
|
||||
|
||||
All the dependencies we have seen are a fixed function or class.
|
||||
|
||||
But there could be cases where you want to be able to set parameters on the dependency, without having to declare many different functions or classes.
|
||||
|
||||
Let's imagine that we want to have a dependency that checks if the query parameter `q` contains some fixed content.
|
||||
|
||||
But we want to be able to parameterize that fixed content.
|
||||
|
||||
## A "callable" instance
|
||||
|
||||
In Python there's a way to make an instance of a class a "callable".
|
||||
|
||||
Not the class itself (which is already a callable), but an instance of that class.
|
||||
|
||||
To do that, we declare a method `__call__`:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
## Parameterize the instance
|
||||
|
||||
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
|
||||
|
||||
## Create an instance
|
||||
|
||||
We could create an instance of this class with:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
|
||||
|
||||
## Use the instance as a dependency
|
||||
|
||||
Then, we could use this `checker` in a `Depends(checker)`, instead of `Depends(FixedContentQueryChecker)`, because the dependency is the instance, `checker`, not the class itself.
|
||||
|
||||
And when solving the dependency, **FastAPI** will call this `checker` like:
|
||||
|
||||
```Python
|
||||
checker(q="somequery")
|
||||
```
|
||||
|
||||
...and pass whatever that returns as the value of the dependency in our path operation function as the parameter `fixed_content_included`:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
All this might seem contrived. And it might not be very clear how is it useful yet.
|
||||
|
||||
These examples are intentionally simple, but show how it all works.
|
||||
|
||||
In the chapters about security, you will be using utility functions that are implemented in this same way.
|
||||
|
||||
If you understood all this, you already know how those utility tools for security work underneath.
|
||||
@@ -0,0 +1,177 @@
|
||||
Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example.
|
||||
|
||||
## A `dict` from the previous example
|
||||
|
||||
In the previous example, we where returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
But then we get a `dict` in the parameter `commons` of the path operation function.
|
||||
|
||||
And we know that `dict`s can't provide a lot of editor support because they can't know their keys and value types.
|
||||
|
||||
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.
|
||||
|
||||
So, if you have an object `something` (that might _not_ be a function) and you can do:
|
||||
|
||||
```Python
|
||||
something()
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```Python
|
||||
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.
|
||||
|
||||
So, a Python class is also a **callable**.
|
||||
|
||||
Then, in **FastAPI**, you could use a Python class as a dependency.
|
||||
|
||||
What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters defined.
|
||||
|
||||
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.
|
||||
|
||||
That also applies to callables with no parameters at all. The same as would be for path operation functions with no parameteres.
|
||||
|
||||
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParameters`:
|
||||
|
||||
```Python hl_lines="9 10 11 12 13"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
Pay attention to the `__init__` method used to create the instance of the class:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
...it has the same parameters as our previous `common_parameters`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
Those parameters are what **FastAPI** will use to "solve" the dependency.
|
||||
|
||||
In both cases, it will have:
|
||||
|
||||
* an optional `q` query parameter.
|
||||
* a `skip` query parameter, with a default of `0`.
|
||||
* a `limit` query parameter, with a default of `100`.
|
||||
|
||||
In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.
|
||||
|
||||
## 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`.
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Type annotation vs `Depends`
|
||||
|
||||
In the code above, you are declaring `commons` as:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
The last `CommonQueryParams`, in:
|
||||
|
||||
```Python
|
||||
... = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...is what **FastAPI** will actually use to know what is the dependency.
|
||||
|
||||
From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call.
|
||||
|
||||
---
|
||||
|
||||
In this case, the first `CommonQueryParams`, in:
|
||||
|
||||
```Python
|
||||
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).
|
||||
|
||||
You could actually write just:
|
||||
|
||||
```Python
|
||||
commons = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
..as in:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
||||
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:
|
||||
|
||||
```Python hl_lines="19 20 21"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Shortcut
|
||||
|
||||
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
|
||||
|
||||
```Python
|
||||
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.
|
||||
|
||||
For those specific cases, you can do the following:
|
||||
|
||||
Instead of writing:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...you write:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends()
|
||||
```
|
||||
|
||||
So, you can declare the dependency as the type of the variable, and use `Depends()` as the "default" value, without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
|
||||
|
||||
So, the same example would look like:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial004.py!}
|
||||
```
|
||||
|
||||
...and **FastAPI** will know what to do.
|
||||
|
||||
!!! 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.
|
||||
+1
-1
@@ -22,7 +22,7 @@ That's it.
|
||||
|
||||
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" (the `@app.get("/some-path")`).
|
||||
You can think of it as a path operation function without the "decorator" (without the `@app.get("/some-path")`).
|
||||
|
||||
And it can return anything you want.
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example.
|
||||
|
||||
## A `dict` from the previous example
|
||||
|
||||
In the previous example, we where returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
But then we get a `dict` in the parameter `commons` of the path operation function.
|
||||
|
||||
And we know that `dict`s can't provide a lot of editor support because they can't know their keys and value types.
|
||||
|
||||
## Create a Pydantic model
|
||||
|
||||
But we are already using Pydantic models in other places and we have already seen all the benefits.
|
||||
|
||||
Let's use them here too.
|
||||
|
||||
Create a model for the common parameters (and don't pay attention to the rest, for now):
|
||||
|
||||
```Python hl_lines="11 12 13 14"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Return a Pydantic model
|
||||
|
||||
Now we can return a Pydantic model from the dependency ("dependable") with the same data as the dict before:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Declare the Pydantic model
|
||||
|
||||
We can now come back to the path operation function and declare the type of the `commons` parameter to be that Pydantic model:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(common_parameters)
|
||||
```
|
||||
|
||||
It won't be interpreted as a JSON request `Body` because we are using `Depends`:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In the case of dependencies with `Depends`, the type of the parameter is only to get editor support.
|
||||
|
||||
Your dependencies won't be enforced to return a specific type of data.
|
||||
|
||||
## Use the Pydantic model
|
||||
|
||||
And now we can use that model in our code, with all the lovable editor support:
|
||||
|
||||
```Python hl_lines="23 24 25"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/dependencies/image02.png">
|
||||
|
||||
## Trees of hierarchical dependencies
|
||||
|
||||
With the **Dependency Injection** system you can build arbitrarily deep trees of hierarchical dependencies (also known as dependency graphs) by having dependencies that also have dependencies themselves.
|
||||
|
||||
You will see examples of these dependency trees in the next chapters about security.
|
||||
|
||||
## Recap
|
||||
|
||||
By using Pydantic models in your dependencies too you can keep all the editor support that **FastAPI** is designed to support.
|
||||
@@ -0,0 +1,60 @@
|
||||
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.
|
||||
|
||||
### First dependency "dependable"
|
||||
|
||||
You could create a first dependency ("dependable") like:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
It declares an optional query parameter `q` as a `str`, and then it just returns it.
|
||||
|
||||
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):
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
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`.
|
||||
* It also declares an optional `last_query` cookie, as a `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.
|
||||
|
||||
### Use the dependency
|
||||
|
||||
Then we can use the dependency with:
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Notice that we are only declaring one dependency in the path operation function, the `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.
|
||||
|
||||
|
||||
## 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.
|
||||
Reference in new issue
Block a user