📝 Refactor docs, "Tutorial - User Guide" and "Advanced User Guide" (#887)

This commit is contained in:
Sebastián Ramírez authored and GitHub committed 2020-01-18 16:21:54 +01:00
1 parent a41a729682
commit 22982287ff
78 files changed
+340 -292

No files matched your search

@@ -1,73 +0,0 @@
!!! warning
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/tutorial011.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.
## 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/tutorial011.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/tutorial011.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/tutorial011.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.
@@ -2,13 +2,13 @@ Before diving deeper into the **Dependency Injection** system, let's upgrade the
## A `dict` from the previous example
In the previous example, we are returning a `dict` from our dependency ("dependable"):
In the previous example, we were 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.
But then we get a `dict` in the parameter `commons` of the *path operation function*.
And we know that editors can't provide a lot of support (like completion) for `dict`s, because they can't know their keys and value types.
@@ -63,9 +63,9 @@ 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.
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 it would be for path operation functions with no parameters.
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`:
@@ -173,7 +173,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
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, 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)`.
So, the same example would look like:
@@ -57,4 +57,4 @@ So, you can re-use a normal dependency (that returns a value) you already use so
## 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*.
Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*.
@@ -10,11 +10,11 @@ To do this, use `yield` instead of `return`, and write the extra steps after.
!!! info
For this to work, you need to use **Python 3.7** or above, or in **Python 3.6**, install the "backports":
```bash
```
pip install async-exit-stack async-generator
```
This installs <a href="https://github.com/sorcio/async_exit_stack" class="external-link" target="_blank">async-exit-stack</a> and <a href="https://github.com/python-trio/async_generator" target="_blank">async-generator</a>.
This installs <a href="https://github.com/sorcio/async_exit_stack" class="external-link" target="_blank">async-exit-stack</a> and <a href="https://github.com/python-trio/async_generator" class="external-link" target="_blank">async-generator</a>.
!!! note "Technical Details"
Any function that is valid to use with:
@@ -116,7 +116,7 @@ with open("./somefile.txt") as f:
print(contents)
```
Underneath, the `open("./somefile.txt")` returns an object that is a called a "Context Manager".
Underneath, the `open("./somefile.txt")` creates an object that is a called a "Context Manager".
When the `with` block finishes, it makes sure to close the file, even if there were exceptions.
@@ -129,9 +129,9 @@ When you create a dependency with `yield`, **FastAPI** will internally convert i
If you are just starting with **FastAPI** you might want to skip it for now.
In Python, you can create context managers by <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">creating a class with two methods: `__enter__()` and `__exit__()`</a>.
In Python, you can create Context Managers by <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">creating a class with two methods: `__enter__()` and `__exit__()`</a>.
You can also use them with **FastAPI** dependencies with `yield` by using
You can also use them inside of **FastAPI** dependencies with `yield` by using
`with` or `async with` statements inside of the dependency function:
```Python hl_lines="1 2 3 4 5 6 7 8 9 13"
@@ -2,9 +2,9 @@
It is designed to be very simple to use, and to make it very easy for any developer to integrate other components with **FastAPI**.
## "Dependency Injection"?
## 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".
**"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).
@@ -13,7 +13,7 @@ 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.
* etc.
* And many other things...
All these, while minimizing code repetition.
@@ -27,7 +27,7 @@ But this way we can focus on how the **Dependency Injection** system works.
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:
It is just a function that can take all the same parameters that a *path operation function* can take:
```Python hl_lines="6 7"
{!./src/dependencies/tutorial001.py!}
@@ -37,9 +37,9 @@ That's it.
**2 lines**.
And it has the same shape and structure that all your path operation functions.
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")`).
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.
@@ -59,7 +59,7 @@ And then it just returns a `dict` containing those values.
### 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:
The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter:
```Python hl_lines="11 16"
{!./src/dependencies/tutorial001.py!}
@@ -71,7 +71,7 @@ You only give `Depends` a single parameter.
This parameter must be something like a function.
And that function takes parameters in the same way that path operation functions do.
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.
@@ -80,26 +80,25 @@ Whenever a new request arrives, **FastAPI** will take care of:
* 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.
* Assign that result to the parameter in your *path operation function*.
!!! note
!!! check
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
You just pass it to `Depends` and **FastAPI** knows how to do the rest.
## To `async` or not to `async`
As dependencies will also be called by **FastAPI** (the same as your path operation functions), the same rules apply while defining your functions.
As dependencies will also be called by **FastAPI** (the same as your *path operation functions*), the same rules apply while defining your functions.
You can use `async def` or normal `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.
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.
It doesn't matter. **FastAPI** will know what to do.
!!! note
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
If you don't know, check the [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} section about `async` and `await` in the docs.
## Integrated with OpenAPI
@@ -118,7 +117,7 @@ Actually, all (or most) of the web frameworks work in this same way.
You never call those functions directly. They are called by your framework (in this case, **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.
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.
Other common terms for this same idea of "dependency injection" are:
@@ -130,9 +129,9 @@ Other common terms for this same idea of "dependency injection" are:
## **FastAPI** plug-ins
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.
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_.
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*.
You will see examples of this in the next chapters, about relational and NoSQL databases, security, etc.
@@ -159,6 +158,6 @@ In the end, a hierarchical tree of dependencies is built, and the **Dependency I
## Integrated with **OpenAPI**
All these dependencies, while declaring their requirements, add parameters, validations, etc. to your path operations.
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.
@@ -1,6 +1,6 @@
You can create dependencies that have sub-dependencies.
You can create dependencies that have **sub-dependencies**.
They can be as "deep" as you need them to be.
They can be as **deep** as you need them to be.
**FastAPI** will take care of solving them.
@@ -40,7 +40,7 @@ Then we can use the dependency with:
```
!!! info
Notice that we are only declaring one dependency in the path operation function, the `query_or_cookie_extractor`.
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.
@@ -61,7 +61,7 @@ async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False
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.
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).