from fastapi import Depends, FastAPI
-from pydantic import BaseModel
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
-class CommonQueryParams(BaseModel):
- q: str = None
- skip: int = None
- limit: int = None
-
-
-async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
- return CommonQueryParams(q=q, skip=skip, limit=limit)
+class CommonQueryParams:
+ def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
+ self.q = q
+ self.skip = skip
+ self.limit = limit
@app.get("/items/")
-async def read_items(commons: CommonQueryParams = Depends(common_parameters)):
+async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
-from typing import List
-
-from fastapi import Cookie, Depends, FastAPI
-from pydantic import BaseModel
+from fastapi import Depends, FastAPI
app = FastAPI()
-class InterestsTracker(BaseModel):
- track_code: str
- interests: List[str]
-
-
-fake_tracked_users_db = {
- "Foo": {"track_code": "Foo", "interests": ["sports", "movies"]},
- "Bar": {"track_code": "Bar", "interests": ["food", "shows"]},
- "Baz": {"track_code": "Baz", "interests": ["gaming", "virtual reality"]},
-}
+fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
-async def get_tracked_interests(track_code: str = Cookie(None)):
- if track_code in fake_tracked_users_db:
- track_dict = fake_tracked_users_db[track_code]
- track = InterestsTracker(**track_dict)
- return track
- return None
+class CommonQueryParams:
+ def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
+ self.q = q
+ self.skip = skip
+ self.limit = limit
-@app.get("/interests/")
-async def read_interests(
- tracked_interests: InterestsTracker = Depends(get_tracked_interests)
-):
- response = {"interests": tracked_interests.interests}
+@app.get("/items/")
+async def read_items(commons=Depends(CommonQueryParams)):
+ response = {}
+ if commons.q:
+ response.update({"q": commons.q})
+ items = fake_items_db[commons.skip : commons.limit]
+ response.update({"items": items})
return response
-from random import choice
-from typing import List
-
-from fastapi import Cookie, Depends, FastAPI
-from pydantic import BaseModel
+from fastapi import Depends, FastAPI
app = FastAPI()
-class InterestsTracker(BaseModel):
- track_code: str
- interests: List[str]
-
-
-fake_tracked_users_db = {
- "Foo": {"track_code": "Foo", "interests": ["sports", "movies"]},
- "Bar": {"track_code": "Bar", "interests": ["food", "shows"]},
- "Baz": {"track_code": "Baz", "interests": ["gaming", "virtual reality"]},
-}
-
-
-async def get_tracked_interests(track_code: str = Cookie(None)):
- if track_code in fake_tracked_users_db:
- track_dict = fake_tracked_users_db[track_code]
- track = InterestsTracker(**track_dict)
- return track
- return None
-
+fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
-class ComplexTracker:
- def __init__(self, tracker: InterestsTracker = Depends(get_tracked_interests)):
- self.tracker = tracker
- def random_interest(self):
- """
- Get a random interest from the tracked ones for the current user.
- If the user doesn't have tracked interests, return a random one from the ones available.
- """
- if self.tracker.interests:
- return choice(self.tracker.interests)
- return choice(
- ["sports", "movies", "food", "shows", "gaming", "virtual reality"]
- )
+class CommonQueryParams:
+ def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
+ self.q = q
+ self.skip = skip
+ self.limit = limit
-@app.get("/suggested-category")
-async def read_suggested_category(tracker: ComplexTracker = Depends(None)):
- response = {"category": tracker.random_interest()}
+@app.get("/items/")
+async def read_items(commons: CommonQueryParams = Depends()):
+ response = {}
+ if commons.q:
+ response.update({"q": commons.q})
+ items = fake_items_db[commons.skip : commons.limit]
+ response.update({"items": items})
return response
--- /dev/null
+from fastapi import Cookie, Depends, FastAPI
+
+app = FastAPI()
+
+
+def query_extractor(q: str = None):
+ return q
+
+
+def query_or_cookie_extractor(
+ q: str = Depends(query_extractor), last_query: str = Cookie(None)
+):
+ if not q:
+ return last_query
+ return q
+
+
+@app.get("/items/")
+async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
+ return {"q_or_cookie": query_or_default}
--- /dev/null
+from fastapi import Depends, FastAPI
+
+app = FastAPI()
+
+
+class FixedContentQueryChecker:
+ def __init__(self, fixed_content: str):
+ self.fixed_content = fixed_content
+
+ def __call__(self, q: str = ""):
+ if q:
+ return self.fixed_content in q
+ return False
+
+
+checker = FixedContentQueryChecker("bar")
+
+
+@app.get("/query-checker/")
+async def read_query_check(fixed_content_included: bool = Depends(checker)):
+ return {"fixed_content_in_query": fixed_content_included}
--- /dev/null
+!!! 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.
--- /dev/null
+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.
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.
+++ /dev/null
-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.
\ No newline at end of file
--- /dev/null
+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.
- Custom Response: 'tutorial/custom-response.md'
- Dependencies:
- Dependencies Intro: 'tutorial/dependencies/intro.md'
- - First Steps: 'tutorial/dependencies/first-steps.md'
- - Second Steps: 'tutorial/dependencies/second-steps.md'
- - SQL (Relational) Databases: 'tutorial/sql-databases.md'
- - NoSQL (Distributed / Big Data) Databases: 'tutorial/nosql-databases.md'
+ - First Steps - Functions: 'tutorial/dependencies/first-steps-functions.md'
+ - Classes as Dependencies: 'tutorial/dependencies/classes-as-dependencies.md'
+ - Sub-dependencies: 'tutorial/dependencies/sub-dependencies.md'
+ - Advanced Dependencies: 'tutorial/dependencies/advanced-dependencies.md'
- Security:
- Security Intro: 'tutorial/security/intro.md'
- First Steps: 'tutorial/security/first-steps.md'
- Simple OAuth2 with Password and Bearer: 'tutorial/security/simple-oauth2.md'
- OAuth2 with Password (and hashing), Bearer with JWT tokens: 'tutorial/security/oauth2-jwt.md'
+ - SQL (Relational) Databases: 'tutorial/sql-databases.md'
+ - NoSQL (Distributed / Big Data) Databases: 'tutorial/nosql-databases.md'
- Bigger Applications - Multiple Files: 'tutorial/bigger-applications.md'
- Application Configuration: 'tutorial/application-configuration.md'
- Extra Starlette options: 'tutorial/extra-starlette.md'