Add parameter dependencies to path operation decorators and include_router (#235)

*  Implement dependencies in decorator and .include_router

* 📝 Add docs for parameter dependencies

*  Add tests for dependencies parameter

* 🔥 Remove debugging prints in tests

* 📝 Update release notes
This commit is contained in:
Sebastián Ramírez
2019-05-16 18:07:00 +04:00
committed by GitHub
parent 7c50025c47
commit e92b43b5c8
16 changed files with 472 additions and 67 deletions

View File

@@ -22,16 +22,15 @@ Let's say you have a file structure like this:
!!! tip
There are two `__init__.py` files: one in each directory or subdirectory.
This is what allows importing code from one file into another.
For example, in `app/main.py` you could have a line like:
```
from app.routers import items
```
* The `app` directory contains everything.
* This `app` directory has an empty file `app/__init__.py`.
* So, the `app` directory is a "Python package" (a collection of "Python modules").
@@ -107,7 +106,7 @@ And we don't want to have to explicitly type `/items/` and `tags=["items"]` in e
{!./src/bigger_applications/app/routers/items.py!}
```
### Add some custom `tags` and `responses`
### Add some custom `tags`, `responses`, and `dependencies`
We are not adding the prefix `/items/` nor the `tags=["items"]` to add them later.
@@ -197,12 +196,11 @@ So, to be able to use both of them in the same file, we import the submodules di
{!./src/bigger_applications/app/main.py!}
```
### Include an `APIRouter`
Now, let's include the `router` from the submodule `users`:
```Python hl_lines="7"
```Python hl_lines="13"
{!./src/bigger_applications/app/main.py!}
```
@@ -221,13 +219,12 @@ It will include all the routes from that router as part of it.
!!! 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`, and `responses`
### Include an `APIRouter` with a `prefix`, `tags`, `responses`, and `dependencies`
Now, let's include the router from the `items` submodule.
@@ -251,7 +248,9 @@ We can also add a list of `tags` that will be applied to all the *path operation
And we can add predefined `responses` that will be included in all the *path operations* too.
```Python hl_lines="8 9 10 11 12 13"
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.
```Python hl_lines="8 9 10 14 15 16 17 18 19 20"
{!./src/bigger_applications/app/main.py!}
```
@@ -262,27 +261,28 @@ The end result is that the item paths are now:
...as we intended.
They will be marked with a list of tags that contain a single string `"items"`.
* They will be marked with a list of tags that contain a single string `"items"`.
* The *path operation* that declared a `"custom"` tag will have both tags, `items` and `custom`.
* These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
* All of them will include the predefined `responses`.
* The *path operation* that declared a custom `403` response will have both the predefined responses (`404`) and the `403` declared in it directly.
* All these *path operations* will have the list of `dependencies` evaluated/executed before them.
* If you also declare dependencies in a specific *path operation*, **they will be executed too**.
* The router dependencies are executed first, then the <a href="https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-decorator/" target="_blank">`dependencies` in the decorator</a>, and then the normal parameter dependencies.
* You can also add <a href="https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/" target="_blank">`Security` dependencies with `scopes`</a>.
The *path operation* that declared a `"custom"` tag will have both tags, `items` and `custom`.
These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
And all of them will include the the predefined `responses`.
The *path operation* that declared a custom `403` response will have both the predefined responses (`404`) and the `403` declared in it directly.
!!! 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.
!!! check
The `prefix`, `tags`, and `responses` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
The `prefix`, `tags`, `responses` and `dependencies` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
!!! tip
You could also add path operations directly, for example with: `@app.get(...)`.
Apart from `app.include_router()`, in the same **FastAPI** app.
It would still work the same.
Apart from `app.include_router()`, in the same **FastAPI** app.
It would still work the same.
!!! info "Very Technical Details"
**Note**: this is a very technical detail that you probably can **just skip**.

View File

@@ -22,7 +22,7 @@ Not the class itself (which is already a callable), but an instance of that clas
To do that, we declare a method `__call__`:
```Python hl_lines="10"
{!./src/dependencies/tutorial006.py!}
{!./src/dependencies/tutorial007.py!}
```
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
@@ -32,7 +32,7 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
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!}
{!./src/dependencies/tutorial007.py!}
```
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
@@ -42,7 +42,7 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
We could create an instance of this class with:
```Python hl_lines="16"
{!./src/dependencies/tutorial006.py!}
{!./src/dependencies/tutorial007.py!}
```
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
@@ -60,7 +60,7 @@ 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!}
{!./src/dependencies/tutorial007.py!}
```
!!! tip

View File

@@ -0,0 +1,60 @@
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*.
## Add `dependencies` to the *path operation decorator*
The *path operation decorator* receives an optional argument `dependencies`.
It should be a `list` of `Depends()`:
```Python hl_lines="17"
{!./src/dependencies/tutorial006.py!}
```
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.
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:
```Python hl_lines="6 11"
{!./src/dependencies/tutorial006.py!}
```
### Raise exceptions
These dependencies can `raise` exceptions, the same as normal dependencies:
```Python hl_lines="8 13"
{!./src/dependencies/tutorial006.py!}
```
### 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:
```Python hl_lines="9 14"
{!./src/dependencies/tutorial006.py!}
```
## Dependencies for a group of *path operations*
Later, when reading about how to <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">structure bigger applications</a>, possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*.

View File

@@ -244,3 +244,7 @@ The most secure is the code flow, but is more complex to implement as it require
But in the end, they are implementing the same OAuth2 standard.
**FastAPI** includes utilities for all these OAuth2 authentication flows in `fastapi.security.oauth2`.
## `Security` in decorator `dependencies`
The same way you can define a `list` of <a href="https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-decorator/" target="_blank">`Depends` in the decorator's `dependencies` parameter</a>, you could also use `Security` with `scopes` there.