* Updated .py files with Optional tag (up to body_nested_models) * Update optionals * docs_src/ all updates, few I was unsure of * Updated markdown files with Optional param * es: Add Optional typing to index.md * Last of markdown files updated with Optional param * Update highlight lines * it: Add Optional typings * README.md: Update with Optional typings * Update more highlight increments * Update highlights * schema-extra-example.md: Update highlights * updating highlighting on website to reflect .py changes * Update highlighting for query-params & response-directly * Address PR comments * Get rid of unnecessary comment * ⏪ Revert Optional in Chinese docs as it probably also requires changes in text * 🎨 Apply format * ⏪ Revert modified example * ♻️ Simplify example in docs * 📝 Update OpenAPI callback example to use Optional * ✨ Add Optional types to tests * 📝 Update docs about query params, default to using Optional * 🎨 Update code examples line highlighting * 📝 Update nested models docs to use "type parameters" instead of "subtypes" * 📝 Add notes about FastAPI usage of None including: = None and = Query(None) and clarify relationship with Optional[str] * 📝 Add note about response_model_by_alias * ♻️ Simplify query param list example * 🔥 Remove test for removed example * ✅ Update test for updated example Co-authored-by: Christopher Nguyen <chrisngyn99@gmail.com> Co-authored-by: yk396 <yk396@cornell.edu> Co-authored-by: Kai Chen <kaichen120@gmail.com>
5.5 KiB
Classes as Dependencies
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 were returning a dict from our dependency ("dependable"):
{!../../../docs_src/dependencies/tutorial001.py!}
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 dicts, 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 "call" it (execute it) like:
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.
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.
And to create fluffy, you are "calling" Cat.
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 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:
{!../../../docs_src/dependencies/tutorial002.py!}
Pay attention to the __init__ method used to create the instance of the class:
{!../../../docs_src/dependencies/tutorial002.py!}
...it has the same parameters as our previous common_parameters:
{!../../../docs_src/dependencies/tutorial001.py!}
Those parameters are what FastAPI will use to "solve" the dependency.
In both cases, it will have:
- an optional
qquery parameter. - a
skipquery parameter, with a default of0. - a
limitquery parameter, with a default of100.
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.
{!../../../docs_src/dependencies/tutorial002.py!}
Type annotation vs Depends
In the code above, you are declaring commons as:
commons: CommonQueryParams = Depends(CommonQueryParams)
The last CommonQueryParams, in:
... = 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:
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:
commons = Depends(CommonQueryParams)
..as in:
{!../../../docs_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:
Shortcut
But you see that we are having some code repetition here, writing CommonQueryParams twice:
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:
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).
So, the same example would look like:
{!../../../docs_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.