mirror of https://github.com/tiangolo/fastapi.git
✨ Add docs and tests for Python 3.9 and Python 3.10 (#3712)
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
This commit is contained in:
parent
83f6781037
commit
d08a062ee2
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -148,37 +148,62 @@ You can use, for example:
|
|||
|
||||
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
|
||||
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`.
|
||||
These types that have internal types are called "**generic**" types. And it's possible to declare them, even with their internal types.
|
||||
|
||||
It exists specifically to support these type hints.
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`. It exists specifically to support these type hints.
|
||||
|
||||
#### `List`
|
||||
#### Newer versions of Python
|
||||
|
||||
The syntax using `typing` is **compatible** with all versions, from Python 3.6 to the latest ones, including Python 3.9, Python 3.10, etc.
|
||||
|
||||
As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations.
|
||||
|
||||
If you can chose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity. See some examples below.
|
||||
|
||||
#### List
|
||||
|
||||
For example, let's define a variable to be a `list` of `str`.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
From `typing`, import `List` (with a capital `L`):
|
||||
|
||||
``` Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
|
||||
As the type, put the `List`.
|
||||
As the type, put the `List` that you imported from `typing`.
|
||||
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
|
||||
As the type, put `list`.
|
||||
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Those internal types in the square brackets are called "type parameters".
|
||||
|
||||
In this case, `str` is the type parameter passed to `List`.
|
||||
In this case, `str` is the type parameter passed to `List` (or `list` in Python 3.9 and above).
|
||||
|
||||
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
|
||||
|
||||
!!! tip
|
||||
If you use Python 3.9 or above, you don't have to import `List` from `typing`, you can use the same regular `list` type instead.
|
||||
|
||||
By doing that, your editor can provide support even while processing items from the list:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
|
@ -189,12 +214,20 @@ Notice that the variable `item` is one of the elements in the list `items`.
|
|||
|
||||
And still, the editor knows it is a `str`, and provides support for that.
|
||||
|
||||
#### `Tuple` and `Set`
|
||||
#### Tuple and Set
|
||||
|
||||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial007.py!}
|
||||
{!> ../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
|
@ -202,7 +235,7 @@ This means:
|
|||
* The variable `items_t` is a `tuple` with 3 items, an `int`, another `int`, and a `str`.
|
||||
* The variable `items_s` is a `set`, and each of its items is of type `bytes`.
|
||||
|
||||
#### `Dict`
|
||||
#### Dict
|
||||
|
||||
To define a `dict`, you pass 2 type parameters, separated by commas.
|
||||
|
||||
|
|
@ -210,8 +243,16 @@ The first type parameter is for the keys of the `dict`.
|
|||
|
||||
The second type parameter is for the values of the `dict`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial008.py!}
|
||||
{!> ../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
|
@ -220,9 +261,33 @@ This means:
|
|||
* The keys of this `dict` are of type `str` (let's say, the name of each item).
|
||||
* The values of this `dict` are of type `float` (let's say, the price of each item).
|
||||
|
||||
#### `Optional`
|
||||
#### Union
|
||||
|
||||
You can also use `Optional` to declare that a variable has a type, like `str`, but that it is "optional", which means that it could also be `None`:
|
||||
You can declare that a variable can be any of **several types**, for example, an `int` or a `str`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept.
|
||||
|
||||
In Python 3.10 there's also an **alternative syntax** were you can put the possible types separated by a <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr>.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008b.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
In both cases this means that `item` could be an `int` or a `str`.
|
||||
|
||||
#### Possibly `None`
|
||||
|
||||
You can declare that a value could have a type, like `str`, but that it could also be `None`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
|
|
@ -230,18 +295,73 @@ You can also use `Optional` to declare that a variable has a type, like `str`, b
|
|||
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detecting errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent.
|
||||
|
||||
This also means that in Python 3.10, you can use `Something | None`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6 and above - alternative"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009b.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
#### Generic types
|
||||
|
||||
These types that take type parameters in square brackets, like:
|
||||
These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
are called **Generic types** or **Generics**.
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
You can use the same builtin types as generics (with square brakets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And the same as with Python 3.6, from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
You can use the same builtin types as generics (with square brakets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And the same as with Python 3.6, from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional` (the same as with Python 3.6)
|
||||
* ...and others.
|
||||
|
||||
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr> to declare unions of types.
|
||||
|
||||
### Classes as types
|
||||
|
||||
|
|
@ -275,10 +395,24 @@ Then you create an instance of that class with some values and it will validate
|
|||
|
||||
And you get all the editor support with that resulting object.
|
||||
|
||||
Taken from the official Pydantic docs:
|
||||
An example from the official Pydantic docs:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial011.py!}
|
||||
{!> ../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -57,8 +57,16 @@ Using `BackgroundTasks` also works with the dependency injection system, you can
|
|||
|
||||
**FastAPI** knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13 15 22 25"
|
||||
{!../../../docs_src/background_tasks/tutorial002.py!}
|
||||
{!> ../../../docs_src/background_tasks/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="11 13 20 23"
|
||||
{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
In this example, the messages will be written to the `log.txt` file *after* the response is sent.
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ The same way you can declare additional validation and metadata in *path operati
|
|||
|
||||
First, you have to import it:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body_fields/tutorial001.py!}
|
||||
{!> ../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
@ -17,8 +25,16 @@ First, you have to import it:
|
|||
|
||||
You can then use `Field` with model attributes:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="11-14"
|
||||
{!../../../docs_src/body_fields/tutorial001.py!}
|
||||
{!> ../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="9-12"
|
||||
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
|
||||
|
|
|
|||
|
|
@ -8,8 +8,16 @@ First, of course, you can mix `Path`, `Query` and request body parameter declara
|
|||
|
||||
And you can also declare body parameters as optional, by setting the default to `None`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19-21"
|
||||
{!../../../docs_src/body_multiple_params/tutorial001.py!}
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-19"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -30,8 +38,16 @@ In the previous example, the *path operations* would expect a JSON body with the
|
|||
|
||||
But you can also declare multiple body parameters, e.g. `item` and `user`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/body_multiple_params/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** will notice that there are more than one body parameters in the function (two parameters that are Pydantic models).
|
||||
|
|
@ -71,14 +87,20 @@ If you declare it as is, because it is a singular value, **FastAPI** will assume
|
|||
|
||||
But you can instruct **FastAPI** to treat it as another body key using `Body`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="23"
|
||||
{!../../../docs_src/body_multiple_params/tutorial003.py!}
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** will expect a body like:
|
||||
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": {
|
||||
|
|
@ -107,10 +129,24 @@ As, by default, singular values are interpreted as query parameters, you don't h
|
|||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
as in:
|
||||
Or in Python 3.10 and above:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="28"
|
||||
{!../../../docs_src/body_multiple_params/tutorial004.py!}
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="26"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -131,8 +167,16 @@ item: Item = Body(..., embed=True)
|
|||
|
||||
as in:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!../../../docs_src/body_multiple_params/tutorial005.py!}
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
In this case **FastAPI** will expect a body like:
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ With **FastAPI**, you can define, validate, document, and use arbitrarily deeply
|
|||
|
||||
You can define an attribute to be a subtype. For example, a Python `list`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!../../../docs_src/body_nested_models/tutorial001.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
This will make `tags` be a list of items. Although it doesn't declare the type of each of the items.
|
||||
|
|
@ -18,19 +26,29 @@ But Python has a specific way to declare lists with internal types, or "type par
|
|||
|
||||
### Import typing's `List`
|
||||
|
||||
First, import `List` from standard Python's `typing` module:
|
||||
In Python 3.9 and above you can use the standard `list` to declare these type annotations as we'll see below. 💡
|
||||
|
||||
But in Python versions before 3.9 (3.6 and above), you first need to import `List` from standard Python's `typing` module:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Declare a `List` with a type parameter
|
||||
### Declare a `list` with a type parameter
|
||||
|
||||
To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`:
|
||||
|
||||
* Import them from the `typing` module
|
||||
* If you are in a Python version lower than 3.9, import their equivalent version from the `typing` module
|
||||
* Pass the internal type(s) as "type parameters" using square brackets: `[` and `]`
|
||||
|
||||
In Python 3.9 it would be:
|
||||
|
||||
```Python
|
||||
my_list: list[str]
|
||||
```
|
||||
|
||||
In versions of Python before 3.9, it would be:
|
||||
|
||||
```Python
|
||||
from typing import List
|
||||
|
||||
|
|
@ -43,8 +61,22 @@ Use that same standard syntax for model attributes with internal types.
|
|||
|
||||
So, in our example, we can make `tags` be specifically a "list of strings":
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Set types
|
||||
|
|
@ -53,10 +85,24 @@ But then we think about it, and realize that tags shouldn't repeat, they would p
|
|||
|
||||
And Python has a special data type for sets of unique items, the `set`.
|
||||
|
||||
Then we can import `Set` and declare `tags` as a `set` of `str`:
|
||||
Then we can declare `tags` as a set of strings:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 14"
|
||||
{!../../../docs_src/body_nested_models/tutorial003.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial003_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
|
||||
|
|
@ -79,16 +125,44 @@ All that, arbitrarily nested.
|
|||
|
||||
For example, we can define an `Image` model:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9-11"
|
||||
{!../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9-11"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7-9"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
### Use the submodel as a type
|
||||
|
||||
And then we can use it as the type of an attribute:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
This would mean that **FastAPI** would expect a body similar to:
|
||||
|
|
@ -122,8 +196,22 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
|
|||
|
||||
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
{!../../../docs_src/body_nested_models/tutorial005.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2 8"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
|
||||
|
|
@ -132,8 +220,22 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
|
|||
|
||||
You can also use Pydantic models as subtypes of `list`, `set`, etc:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!../../../docs_src/body_nested_models/tutorial006.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!}
|
||||
```
|
||||
|
||||
This will expect (convert, validate, document, etc) a JSON body like:
|
||||
|
|
@ -169,8 +271,22 @@ This will expect (convert, validate, document, etc) a JSON body like:
|
|||
|
||||
You can define arbitrarily deeply nested models:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!../../../docs_src/body_nested_models/tutorial007.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 12 18 21 25"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -184,10 +300,24 @@ If the top level value of the JSON body you expect is a JSON `array` (a Python `
|
|||
images: List[Image]
|
||||
```
|
||||
|
||||
or in Python 3.9 and above:
|
||||
|
||||
```Python
|
||||
images: list[Image]
|
||||
```
|
||||
|
||||
as in:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!../../../docs_src/body_nested_models/tutorial008.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial008.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
## Editor support everywhere
|
||||
|
|
@ -218,8 +348,16 @@ That's what we are going to see here.
|
|||
|
||||
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/body_nested_models/tutorial009.py!}
|
||||
{!> ../../../docs_src/body_nested_models/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/body_nested_models/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
|
|||
|
|
@ -6,8 +6,22 @@ To update an item you can use the <a href="https://developer.mozilla.org/en-US/d
|
|||
|
||||
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="30-35"
|
||||
{!../../../docs_src/body_updates/tutorial001.py!}
|
||||
{!> ../../../docs_src/body_updates/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="30-35"
|
||||
{!> ../../../docs_src/body_updates/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="28-33"
|
||||
{!> ../../../docs_src/body_updates/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
`PUT` is used to receive data that should replace the existing data.
|
||||
|
|
@ -53,8 +67,22 @@ That would generate a `dict` with only the data that was set when creating the `
|
|||
|
||||
Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="32"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
### Using Pydantic's `update` parameter
|
||||
|
|
@ -63,8 +91,22 @@ Now, you can create a copy of the existing model using `.copy()`, and pass the `
|
|||
|
||||
Like `stored_item_model.copy(update=update_data)`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="33"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
### Partial updates recap
|
||||
|
|
@ -82,8 +124,22 @@ In summary, to apply partial updates you would:
|
|||
* Save the data to your DB.
|
||||
* Return the updated model.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="30-37"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
{!> ../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="30-37"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="28-35"
|
||||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
|
|||
|
|
@ -19,8 +19,16 @@ To declare a **request** body, you use <a href="https://pydantic-docs.helpmanual
|
|||
|
||||
First, you need to import `BaseModel` from `pydantic`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
{!> ../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!> ../../../docs_src/body/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Create your data model
|
||||
|
|
@ -29,8 +37,16 @@ Then you declare your data model as a class that inherits from `BaseModel`.
|
|||
|
||||
Use standard Python types for all the attributes:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="7-11"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
{!> ../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="5-9"
|
||||
{!> ../../../docs_src/body/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional.
|
||||
|
|
@ -59,8 +75,16 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
|
|||
|
||||
To add it to your *path operation*, declare it the same way you declared path and query parameters:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
{!> ../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!> ../../../docs_src/body/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
...and declare its type as the model you created, `Item`.
|
||||
|
|
@ -125,8 +149,16 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
|
|||
|
||||
Inside of the function, you can access all the attributes of the model object directly:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/body/tutorial002.py!}
|
||||
{!> ../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/body/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Request body + path parameters
|
||||
|
|
@ -135,8 +167,16 @@ You can declare path parameters and request body at the same time.
|
|||
|
||||
**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/body/tutorial003.py!}
|
||||
{!> ../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15-16"
|
||||
{!> ../../../docs_src/body/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
## Request body + path + query parameters
|
||||
|
|
@ -145,8 +185,16 @@ You can also declare **body**, **path** and **query** parameters, all at the sam
|
|||
|
||||
**FastAPI** will recognize each of them and take the data from the correct place.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial004.py!}
|
||||
{!> ../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!> ../../../docs_src/body/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
The function parameters will be recognized as follows:
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ You can define Cookie parameters the same way you define `Query` and `Path` para
|
|||
|
||||
First import `Cookie`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/cookie_params/tutorial001.py!}
|
||||
{!> ../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Declare `Cookie` parameters
|
||||
|
|
@ -16,8 +24,16 @@ Then declare the cookie parameters using the same structure as with `Path` and `
|
|||
|
||||
The first value is the default value, you can pass all the extra validation or annotation parameters:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/cookie_params/tutorial001.py!}
|
||||
{!> ../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ Before diving deeper into the **Dependency Injection** system, let's upgrade the
|
|||
|
||||
In the previous example, we were returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
But then we get a `dict` in the parameter `commons` of the *path operation function*.
|
||||
|
|
@ -71,20 +79,44 @@ That also applies to callables with no parameters at all. The same as it would b
|
|||
|
||||
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="11-15"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="9-13"
|
||||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
Pay attention to the `__init__` method used to create the instance of the class:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
...it has the same parameters as our previous `common_parameters`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
Those parameters are what **FastAPI** will use to "solve" the dependency.
|
||||
|
|
@ -101,8 +133,16 @@ In both cases the data will be converted, validated, documented on the OpenAPI s
|
|||
|
||||
Now you can declare your dependency using this class.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function.
|
||||
|
|
@ -143,8 +183,16 @@ commons = Depends(CommonQueryParams)
|
|||
|
||||
..as in:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial003.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial003_py310.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:
|
||||
|
|
@ -179,8 +227,16 @@ You declare the dependency as the type of the parameter, and you use `Depends()`
|
|||
|
||||
The same example would then look like:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial004.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
...and **FastAPI** will know what to do.
|
||||
|
|
|
|||
|
|
@ -31,8 +31,16 @@ 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:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8-9"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6-7"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
That's it.
|
||||
|
|
@ -55,16 +63,32 @@ And then it just returns a `dict` containing those values.
|
|||
|
||||
### Import `Depends`
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
### 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:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13 18"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="11 16"
|
||||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
Although you use `Depends` in the parameters of your function the same way you use `Body`, `Query`, etc, `Depends` works a bit differently.
|
||||
|
|
|
|||
|
|
@ -6,24 +6,40 @@ They can be as **deep** as you need them to be.
|
|||
|
||||
**FastAPI** will take care of solving them.
|
||||
|
||||
### First dependency "dependable"
|
||||
## First dependency "dependable"
|
||||
|
||||
You could create a first dependency ("dependable") like:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8-9"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6-7"
|
||||
{!> ../../../docs_src/dependencies/tutorial005_py310.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"
|
||||
## 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 3.6 and above"
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
Let's focus on the parameters declared:
|
||||
|
|
@ -33,12 +49,20 @@ Let's focus on the parameters declared:
|
|||
* It also declares an optional `last_query` cookie, as a `str`.
|
||||
* If the user didn't provide any query `q`, we use the last query used, which we saved to a cookie before.
|
||||
|
||||
### Use the dependency
|
||||
## Use the dependency
|
||||
|
||||
Then we can use the dependency with:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
{!> ../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -20,8 +20,16 @@ You can use `jsonable_encoder` for that.
|
|||
|
||||
It receives an object, like a Pydantic model, and returns a JSON compatible version:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="5 22"
|
||||
{!../../../docs_src/encoder/tutorial001.py!}
|
||||
{!> ../../../docs_src/encoder/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="4 21"
|
||||
{!> ../../../docs_src/encoder/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`.
|
||||
|
|
|
|||
|
|
@ -55,12 +55,28 @@ Here are some of the additional data types you can use:
|
|||
|
||||
Here's an example *path operation* with parameters using some of the above types.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 3 12-16"
|
||||
{!../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 2 11-15"
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18-19"
|
||||
{!../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -17,8 +17,16 @@ This is especially the case for user models, because:
|
|||
|
||||
Here's a general idea of how the models could look like with their password fields and the places where they are used:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
|
||||
{!../../../docs_src/extra_models/tutorial001.py!}
|
||||
{!> ../../../docs_src/extra_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39"
|
||||
{!> ../../../docs_src/extra_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
### About `**user_in.dict()`
|
||||
|
|
@ -150,8 +158,16 @@ All the data conversion, validation, documentation, etc. will still work as norm
|
|||
|
||||
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 15-16 19-20 23-24"
|
||||
{!../../../docs_src/extra_models/tutorial002.py!}
|
||||
{!> ../../../docs_src/extra_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 13-14 17-18 21-22"
|
||||
{!> ../../../docs_src/extra_models/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## `Union` or `anyOf`
|
||||
|
|
@ -165,18 +181,48 @@ To do that, use the standard Python type hint <a href="https://docs.python.org/3
|
|||
!!! note
|
||||
When defining a <a href="https://pydantic-docs.helpmanual.io/usage/types/#unions" class="external-link" target="_blank">`Union`</a>, include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 14-15 18-20 33"
|
||||
{!../../../docs_src/extra_models/tutorial003.py!}
|
||||
{!> ../../../docs_src/extra_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 14-15 18-20 33"
|
||||
{!> ../../../docs_src/extra_models/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### `Union` in Python 3.10
|
||||
|
||||
In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`.
|
||||
|
||||
Because we are passing it as a **value to an argument** instead of putting it in a **type annotation**, we have to use `Union` even in Python 3.10.
|
||||
|
||||
If it was in a type annotation we could have used the vertical bar, as:
|
||||
|
||||
```Python
|
||||
some_variable: PlaneItem | CarItem
|
||||
```
|
||||
|
||||
But if we put that in `response_model=PlaneItem | CarItem` we would get an error, because Python would try to perform an **invalid operation** between `PlaneItem` and `CarItem` instead of interpreting that as a type annotation.
|
||||
|
||||
## List of models
|
||||
|
||||
The same way, you can declare responses of lists of objects.
|
||||
|
||||
For that, use the standard Python `typing.List`:
|
||||
For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above):
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 20"
|
||||
{!../../../docs_src/extra_models/tutorial004.py!}
|
||||
{!> ../../../docs_src/extra_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/extra_models/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
## Response with arbitrary `dict`
|
||||
|
|
@ -185,10 +231,18 @@ You can also declare a response using a plain arbitrary `dict`, declaring just t
|
|||
|
||||
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
|
||||
|
||||
In this case, you can use `typing.Dict`:
|
||||
In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above):
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="1 8"
|
||||
{!../../../docs_src/extra_models/tutorial005.py!}
|
||||
{!> ../../../docs_src/extra_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!> ../../../docs_src/extra_models/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ You can define Header parameters the same way you define `Query`, `Path` and `Co
|
|||
|
||||
First import `Header`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/header_params/tutorial001.py!}
|
||||
{!> ../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Declare `Header` parameters
|
||||
|
|
@ -16,8 +24,16 @@ Then declare the header parameters using the same structure as with `Path`, `Que
|
|||
|
||||
The first value is the default value, you can pass all the extra validation or annotation parameters:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/header_params/tutorial001.py!}
|
||||
{!> ../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
|
|
@ -44,14 +60,21 @@ So, you can use `user_agent` as you normally would in Python code, instead of ne
|
|||
|
||||
If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/header_params/tutorial002.py!}
|
||||
{!> ../../../docs_src/header_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/header_params/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores.
|
||||
|
||||
|
||||
## Duplicate headers
|
||||
|
||||
It is possible to receive duplicate headers. That means, the same header with multiple values.
|
||||
|
|
@ -62,8 +85,22 @@ You will receive all the values from the duplicate header as a Python `list`.
|
|||
|
||||
For example, to declare a header of `X-Token` that can appear more than once, you can write:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/header_params/tutorial003.py!}
|
||||
{!> ../../../docs_src/header_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/header_params/tutorial003_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/header_params/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
If you communicate with that *path operation* sending two HTTP headers like:
|
||||
|
|
|
|||
|
|
@ -13,8 +13,22 @@ You can pass directly the `int` code, like `404`.
|
|||
|
||||
But if you don't remember what each number code is for, you can use the shortcut constants in `status`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3 17"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial001.py!}
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="3 17"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 15"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
That status code will be used in the response and will be added to the OpenAPI schema.
|
||||
|
|
@ -28,8 +42,22 @@ That status code will be used in the response and will be added to the OpenAPI s
|
|||
|
||||
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial002.py!}
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15 20 25"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
They will be added to the OpenAPI schema and used by the automatic documentation interfaces:
|
||||
|
|
@ -40,8 +68,22 @@ They will be added to the OpenAPI schema and used by the automatic documentation
|
|||
|
||||
You can add a `summary` and `description`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="20-21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial003.py!}
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="20-21"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="18-19"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
## Description from docstring
|
||||
|
|
@ -50,8 +92,22 @@ As descriptions tend to be long and cover multiple lines, you can declare the *p
|
|||
|
||||
You can write <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19-27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial004.py!}
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="19-27"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-25"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
It will be used in the interactive docs:
|
||||
|
|
@ -62,8 +118,22 @@ It will be used in the interactive docs:
|
|||
|
||||
You can specify the response description with the parameter `response_description`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial005.py!}
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ The same way you can declare more validations and metadata for query parameters
|
|||
|
||||
First, import `Path` from `fastapi`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
## Declare metadata
|
||||
|
|
@ -16,8 +24,16 @@ You can declare all the same parameters as for `Query`.
|
|||
|
||||
For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
|
|||
|
|
@ -4,11 +4,19 @@
|
|||
|
||||
Let's take this application as example:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial001.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
The query parameter `q` is of type `Optional[str]`, that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
The query parameter `q` is of type `Optional[str]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
|
|
@ -23,16 +31,32 @@ We are going to enforce that even though `q` is optional, whenever it is provide
|
|||
|
||||
To achieve that, first import `Query` from `fastapi`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Use `Query` as the default value
|
||||
|
||||
And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
As we have to replace the default value `None` with `Query(None)`, the first parameter to `Query` serves the same purpose of defining that default value.
|
||||
|
|
@ -49,10 +73,22 @@ q: Optional[str] = Query(None)
|
|||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
And in Python 3.10 and above:
|
||||
|
||||
```Python
|
||||
q: str | None = Query(None)
|
||||
```
|
||||
|
||||
...makes the parameter optional, the same as:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
But it declares it explicitly as being a query parameter.
|
||||
|
||||
!!! info
|
||||
Have in mind that FastAPI cares about the part:
|
||||
Have in mind that the most important part to make a parameter optional is the part:
|
||||
|
||||
```Python
|
||||
= None
|
||||
|
|
@ -64,9 +100,9 @@ But it declares it explicitly as being a query parameter.
|
|||
= Query(None)
|
||||
```
|
||||
|
||||
and will use that `None` to detect that the query parameter is not required.
|
||||
as it will use that `None` as the default value, and that way make the parameter **not required**.
|
||||
|
||||
The `Optional` part is only to allow your editor to provide better support.
|
||||
The `Optional` part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required.
|
||||
|
||||
Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings:
|
||||
|
||||
|
|
@ -80,16 +116,32 @@ This will validate the data, show a clear error when the data is not valid, and
|
|||
|
||||
You can also add a parameter `min_length`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial003.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
## Add regular expressions
|
||||
|
||||
You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> that the parameter should match:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial004.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
This specific regular expression checks that the received parameter value:
|
||||
|
|
@ -152,8 +204,22 @@ When you define a query parameter explicitly with `Query` you can also declare i
|
|||
|
||||
For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial011.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial011.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
Then, with a URL like:
|
||||
|
|
@ -186,8 +252,16 @@ The interactive API docs will update accordingly, to allow multiple values:
|
|||
|
||||
And you can also define a default `list` of values if none are provided:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial012.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!}
|
||||
```
|
||||
|
||||
If you go to:
|
||||
|
|
@ -209,7 +283,7 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
|
|||
|
||||
#### Using `list`
|
||||
|
||||
You can also use `list` directly instead of `List[str]`:
|
||||
You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial013.py!}
|
||||
|
|
@ -233,14 +307,30 @@ That information will be included in the generated OpenAPI and used by the docum
|
|||
|
||||
You can add a `title`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial007.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!}
|
||||
```
|
||||
|
||||
And a `description`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="13"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial008.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
|
||||
```
|
||||
|
||||
## Alias parameters
|
||||
|
|
@ -261,8 +351,16 @@ But you still need it to be exactly `item-query`...
|
|||
|
||||
Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial009.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
## Deprecating parameters
|
||||
|
|
@ -273,8 +371,16 @@ You have to leave it there a while because there are clients using it, but you w
|
|||
|
||||
Then pass the parameter `deprecated=True` to `Query`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial010.py!}
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
|
||||
```
|
||||
|
||||
The docs will show it like this:
|
||||
|
|
|
|||
|
|
@ -63,8 +63,16 @@ The parameter values in your function will be:
|
|||
|
||||
The same way, you can declare optional query parameters, by setting their default to `None`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial002.py!}
|
||||
{!> ../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
In this case, the function parameter `q` will be optional, and will be `None` by default.
|
||||
|
|
@ -72,17 +80,20 @@ In this case, the function parameter `q` will be optional, and will be `None` by
|
|||
!!! check
|
||||
Also notice that **FastAPI** is smart enough to notice that the path parameter `item_id` is a path parameter and `q` is not, so, it's a query parameter.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that `q` is optional because of the `= None`.
|
||||
|
||||
The `Optional` in `Optional[str]` is not used by FastAPI (FastAPI will only use the `str` part), but the `Optional[str]` will let your editor help you finding errors in your code.
|
||||
|
||||
## Query parameter type conversion
|
||||
|
||||
You can also declare `bool` types, and they will be converted:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial003.py!}
|
||||
{!> ../../../docs_src/query_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
In this case, if you go to:
|
||||
|
|
@ -126,8 +137,16 @@ And you don't have to declare them in any specific order.
|
|||
|
||||
They will be detected by name:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="8 10"
|
||||
{!../../../docs_src/query_params/tutorial004.py!}
|
||||
{!> ../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6 8"
|
||||
{!> ../../../docs_src/query_params/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
## Required query parameters
|
||||
|
|
@ -184,8 +203,16 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
|||
|
||||
And of course, you can define some parameters as required, some as having a default value, and some entirely optional:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params/tutorial006.py!}
|
||||
{!> ../../../docs_src/query_params/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/query_params/tutorial006_py310.py!}
|
||||
```
|
||||
|
||||
In this case, there are 3 query parameters:
|
||||
|
|
|
|||
|
|
@ -119,10 +119,18 @@ It's possible to upload several files at the same time.
|
|||
|
||||
They would be associated to the same "form field" sent using "form data".
|
||||
|
||||
To use that, declare a `List` of `bytes` or `UploadFile`:
|
||||
To use that, declare a list of `bytes` or `UploadFile`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
{!../../../docs_src/request_files/tutorial002.py!}
|
||||
{!> ../../../docs_src/request_files/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="8 13"
|
||||
{!> ../../../docs_src/request_files/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||
|
|
|
|||
|
|
@ -8,8 +8,22 @@ You can declare the model used for the response with the parameter `response_mod
|
|||
* `@app.delete()`
|
||||
* etc.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!../../../docs_src/response_model/tutorial001.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/response_model/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!> ../../../docs_src/response_model/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -35,14 +49,30 @@ But most importantly:
|
|||
|
||||
Here we are declaring a `UserIn` model, it will contain a plaintext password:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 11"
|
||||
{!../../../docs_src/response_model/tutorial002.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 9"
|
||||
{!> ../../../docs_src/response_model/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
And we are using this model to declare our input and the same model to declare our output:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/response_model/tutorial002.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="15-16"
|
||||
{!> ../../../docs_src/response_model/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
|
||||
|
|
@ -58,20 +88,44 @@ But if we use the same model for another *path operation*, we could be sending o
|
|||
|
||||
We can instead create an input model with the plaintext password and an output model without it:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9 11 16"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7 9 14"
|
||||
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
Here, even though our *path operation function* is returning the same input user that contains the password:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic).
|
||||
|
|
@ -90,8 +144,22 @@ And both models will be used for the interactive API documentation:
|
|||
|
||||
Your response model could have default values, like:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="11 13-14"
|
||||
{!../../../docs_src/response_model/tutorial004.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="11 13-14"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="9 11-12"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
* `description: Optional[str] = None` has a default of `None`.
|
||||
|
|
@ -106,8 +174,22 @@ For example, if you have models with many optional attributes in a NoSQL databas
|
|||
|
||||
You can set the *path operation decorator* parameter `response_model_exclude_unset=True`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!../../../docs_src/response_model/tutorial004.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
and those default values won't be included in the response, only the values actually set.
|
||||
|
|
@ -185,8 +267,16 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
|
|||
|
||||
This also applies to `response_model_by_alias` that works similarly.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!../../../docs_src/response_model/tutorial005.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial005.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
{!> ../../../docs_src/response_model/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -198,8 +288,16 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
|
|||
|
||||
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!../../../docs_src/response_model/tutorial006.py!}
|
||||
{!> ../../../docs_src/response_model/tutorial006.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
{!> ../../../docs_src/response_model/tutorial006_py310.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -8,8 +8,16 @@ Here are several ways to do it.
|
|||
|
||||
You can declare an `example` for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15-23"
|
||||
{!../../../docs_src/schema_extra_example/tutorial001.py!}
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="13-21"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs.
|
||||
|
|
@ -25,8 +33,16 @@ When using `Field()` with Pydantic models, you can also declare extra info for t
|
|||
|
||||
You can use this to add `example` for each field:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4 10-13"
|
||||
{!../../../docs_src/schema_extra_example/tutorial002.py!}
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2 8-11"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
@ -50,8 +66,16 @@ you can also declare a data `example` or a group of `examples` with additional i
|
|||
|
||||
Here we pass an `example` of the data expected in `Body()`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="21-26"
|
||||
{!../../../docs_src/schema_extra_example/tutorial003.py!}
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="19-24"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### Example in the docs UI
|
||||
|
|
@ -73,8 +97,16 @@ Each specific example `dict` in the `examples` can contain:
|
|||
* `value`: This is the actual example shown, e.g. a `dict`.
|
||||
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="22-48"
|
||||
{!../../../docs_src/schema_extra_example/tutorial004.py!}
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="20-46"
|
||||
{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
### Examples in the docs UI
|
||||
|
|
|
|||
|
|
@ -16,8 +16,16 @@ First, let's create a Pydantic user model.
|
|||
|
||||
The same way we use Pydantic to declare bodies, we can use it anywhere else:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="5 12-16"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="3 10-14"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Create a `get_current_user` dependency
|
||||
|
|
@ -30,24 +38,48 @@ Remember that dependencies can have sub-dependencies?
|
|||
|
||||
The same as we were doing before in the *path operation* directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="25"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="23"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Get the user
|
||||
|
||||
`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="19-22 26-27"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="17-20 24-25"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Inject the current user
|
||||
|
||||
So now we can use the same `Depends` with our `get_current_user` in the *path operation*:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="31"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="29"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
Notice that we declare the type of `current_user` as the Pydantic model `User`.
|
||||
|
|
@ -64,7 +96,6 @@ This will help us inside of the function with all the completion and type checks
|
|||
|
||||
We are not restricted to having only one dependency that can return that type of data.
|
||||
|
||||
|
||||
## Other models
|
||||
|
||||
You can now get the current user directly in the *path operation functions* and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`.
|
||||
|
|
@ -81,7 +112,6 @@ You actually don't have users that log in to your application but robots, bots,
|
|||
|
||||
Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system.
|
||||
|
||||
|
||||
## Code size
|
||||
|
||||
This example might seem verbose. Have in mind that we are mixing security, data models, utility functions and *path operations* in the same file.
|
||||
|
|
@ -98,8 +128,16 @@ And all of them (or any portion of them that you want) can take the advantage of
|
|||
|
||||
And all these thousands of *path operations* can be as small as 3 lines:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="30-32"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
{!> ../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="28-30"
|
||||
{!> ../../../docs_src/security/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -109,8 +109,16 @@ And another utility to verify if a received password matches the hash stored.
|
|||
|
||||
And another one to authenticate and return a user.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="7 48 55-56 59-60 69-75"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="6 47 54-55 58-59 68-74"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -144,8 +152,16 @@ Define a Pydantic Model that will be used in the token endpoint for the response
|
|||
|
||||
Create a utility function to generate a new access token.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="6 12-14 28-30 78-86"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="5 11-13 27-29 77-85"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
## Update the dependencies
|
||||
|
|
@ -156,8 +172,16 @@ Decode the received token, verify it, and return the current user.
|
|||
|
||||
If the token is invalid, return an HTTP error right away.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="89-106"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="88-105"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
## Update the `/token` *path operation*
|
||||
|
|
@ -166,8 +190,16 @@ Create a `timedelta` with the expiration time of the token.
|
|||
|
||||
Create a real JWT access token and return it.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="115-128"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
{!> ../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="114-127"
|
||||
{!> ../../../docs_src/security/tutorial004_py310.py!}
|
||||
```
|
||||
|
||||
### Technical details about the JWT "subject" `sub`
|
||||
|
|
|
|||
|
|
@ -49,8 +49,16 @@ Now let's use the utilities provided by **FastAPI** to handle this.
|
|||
|
||||
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="4 76"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="2 74"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
`OAuth2PasswordRequestForm` is a class dependency that declares a form body with:
|
||||
|
|
@ -90,8 +98,16 @@ If there is no such user, we return an error saying "incorrect username or passw
|
|||
|
||||
For the error, we use the exception `HTTPException`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3 77-79"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 75-77"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### Check the password
|
||||
|
|
@ -118,8 +134,16 @@ If your database is stolen, the thief won't have your users' plaintext passwords
|
|||
|
||||
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="80-83"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="78-81"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
#### About `**user_dict`
|
||||
|
|
@ -156,8 +180,16 @@ For this simple example, we are going to just be completely insecure and return
|
|||
|
||||
But for now, let's focus on the specific details we need.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="85"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="83"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -181,8 +213,16 @@ Both of these dependencies will just return an HTTP error if the user doesn't ex
|
|||
|
||||
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
|
||||
|
||||
```Python hl_lines="58-67 69-72 90"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="58-66 69-72 90"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="55-64 67-70 88"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -248,8 +248,22 @@ So, the user will also have a `password` when creating it.
|
|||
|
||||
But for security, the `password` won't be in other Pydantic *models*, for example, it won't be sent from the API when reading a user.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="3 6-8 11-12 23-24 27-28"
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="3 6-8 11-12 23-24 27-28"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="1 4-6 9-10 21-22 25-26"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
#### SQLAlchemy style and Pydantic style
|
||||
|
|
@ -278,8 +292,22 @@ The same way, when reading a user, we can now declare that `items` will contain
|
|||
|
||||
Not only the IDs of those items, but all the data that we defined in the Pydantic *model* for reading items: `Item`.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15-17 31-34"
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="15-17 31-34"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="13-15 29-32"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -293,8 +321,22 @@ This <a href="https://pydantic-docs.helpmanual.io/#config" class="external-link"
|
|||
|
||||
In the `Config` class, set the attribute `orm_mode = True`.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15 19-20 31 36-37"
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="15 19-20 31 36-37"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="13 17-18 29 34-35"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -425,8 +467,16 @@ And now in the file `sql_app/main.py` let's integrate and use all the other part
|
|||
|
||||
In a very simplistic way create the database tables:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
#### Alembic Note
|
||||
|
|
@ -451,8 +501,16 @@ For that, we will create a new dependency with `yield`, as explained before in t
|
|||
|
||||
Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in a single request, and then close it once the request is finished.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="15-20"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="13-18"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -468,8 +526,16 @@ And then, when using the dependency in a *path operation function*, we declare i
|
|||
|
||||
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="24 32 38 47 53"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="22 30 36 45 51"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
!!! info "Technical Details"
|
||||
|
|
@ -481,8 +547,16 @@ This will then give us better editor support inside the *path operation function
|
|||
|
||||
Now, finally, here's the standard **FastAPI** *path operations* code.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="21-26 29-32 35-40 43-47 50-53"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
We are creating the database session before each request in the dependency with `yield`, and then closing it afterwards.
|
||||
|
|
@ -566,8 +640,22 @@ For example, in a background task worker with <a href="https://docs.celeryprojec
|
|||
|
||||
* `sql_app/schemas.py`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
|
||||
```
|
||||
|
||||
* `sql_app/crud.py`:
|
||||
|
|
@ -578,8 +666,16 @@ For example, in a background task worker with <a href="https://docs.celeryprojec
|
|||
|
||||
* `sql_app/main.py`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
|
||||
```
|
||||
|
||||
## Check it
|
||||
|
|
@ -629,8 +725,16 @@ A "middleware" is basically a function that is always executed for each request,
|
|||
|
||||
The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="14-22"
|
||||
{!../../../docs_src/sql_databases/sql_app/alt_main.py!}
|
||||
{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9 and above"
|
||||
|
||||
```Python hl_lines="12-20"
|
||||
{!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Now let's extend this example and add more details to see how to test different
|
|||
|
||||
### Extended **FastAPI** app file
|
||||
|
||||
Let's say you have a file `main_b.py` with your **FastAPI** app.
|
||||
Let's say that now the file `main.py` with your **FastAPI** app has some other **path operations**.
|
||||
|
||||
It has a `GET` operation that could return an error.
|
||||
|
||||
|
|
@ -73,16 +73,24 @@ It has a `POST` operation that could return several errors.
|
|||
|
||||
Both *path operations* require an `X-Token` header.
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/app_testing/main_b.py!}
|
||||
{!> ../../../docs_src/app_testing/app_b/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
|
||||
```
|
||||
|
||||
### Extended testing file
|
||||
|
||||
You could then have a `test_main_b.py`, the same as before, with the extended tests:
|
||||
You could then update `test_main.py` with the extended tests:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/app_testing/test_main_b.py!}
|
||||
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
|
||||
```
|
||||
|
||||
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `requests`.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ theme:
|
|||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from fastapi.testclient import TestClient
|
||||
|
||||
from .main_b import app
|
||||
from .main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from fastapi import FastAPI, Header, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
fake_secret_token = "coneofsilence"
|
||||
|
||||
fake_db = {
|
||||
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
|
||||
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
|
||||
}
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_main(item_id: str, x_token: str = Header(...)):
|
||||
if x_token != fake_secret_token:
|
||||
raise HTTPException(status_code=400, detail="Invalid X-Token header")
|
||||
if item_id not in fake_db:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return fake_db[item_id]
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(item: Item, x_token: str = Header(...)):
|
||||
if x_token != fake_secret_token:
|
||||
raise HTTPException(status_code=400, detail="Invalid X-Token header")
|
||||
if item.id in fake_db:
|
||||
raise HTTPException(status_code=400, detail="Item already exists")
|
||||
fake_db[item.id] = item
|
||||
return item
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
from fastapi.testclient import TestClient
|
||||
|
||||
from .main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_item():
|
||||
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": "foo",
|
||||
"title": "Foo",
|
||||
"description": "There goes my hero",
|
||||
}
|
||||
|
||||
|
||||
def test_read_item_bad_token():
|
||||
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Invalid X-Token header"}
|
||||
|
||||
|
||||
def test_read_inexistent_item():
|
||||
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Item not found"}
|
||||
|
||||
|
||||
def test_create_item():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "coneofsilence"},
|
||||
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": "foobar",
|
||||
"title": "Foo Bar",
|
||||
"description": "The Foo Barters",
|
||||
}
|
||||
|
||||
|
||||
def test_create_item_bad_token():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "hailhydra"},
|
||||
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Invalid X-Token header"}
|
||||
|
||||
|
||||
def test_create_existing_item():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "coneofsilence"},
|
||||
json={
|
||||
"id": "foo",
|
||||
"title": "The Foo ID Stealers",
|
||||
"description": "There goes my stealer",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Item already exists"}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import BackgroundTasks, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def write_log(message: str):
|
||||
with open("log.txt", mode="a") as log:
|
||||
log.write(message)
|
||||
|
||||
|
||||
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
|
||||
if q:
|
||||
message = f"found query: {q}\n"
|
||||
background_tasks.add_task(write_log, message)
|
||||
return q
|
||||
|
||||
|
||||
@app.post("/send-notification/{email}")
|
||||
async def send_notification(
|
||||
email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
|
||||
):
|
||||
message = f"message to {email}\n"
|
||||
background_tasks.add_task(write_log, message)
|
||||
return {"message": "Message sent"}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
async def create_item(item: Item):
|
||||
item_dict = item.dict()
|
||||
if item.tax:
|
||||
price_with_tax = item.price + item.tax
|
||||
item_dict.update({"price_with_tax": price_with_tax})
|
||||
return item_dict
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def create_item(item_id: int, item: Item):
|
||||
return {"item_id": item_id, **item.dict()}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def create_item(item_id: int, item: Item, q: str | None = None):
|
||||
result = {"item_id": item_id, **item.dict()}
|
||||
if q:
|
||||
result.update({"q": q})
|
||||
return result
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = Field(
|
||||
None, title="The description of the item", max_length=300
|
||||
)
|
||||
price: float = Field(..., gt=0, description="The price must be greater than zero")
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from fastapi import FastAPI, Path
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(
|
||||
*,
|
||||
item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
|
||||
q: str | None = None,
|
||||
item: Item | None = None,
|
||||
):
|
||||
results = {"item_id": item_id}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
if item:
|
||||
results.update({"item": item})
|
||||
return results
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
username: str
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item, user: User):
|
||||
results = {"item_id": item_id, "item": item, "user": user}
|
||||
return results
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
username: str
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(
|
||||
item_id: int, item: Item, user: User, importance: int = Body(...)
|
||||
):
|
||||
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
|
||||
return results
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
username: str
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(
|
||||
*,
|
||||
item_id: int,
|
||||
item: Item,
|
||||
user: User,
|
||||
importance: int = Body(..., gt=0),
|
||||
q: str | None = None
|
||||
):
|
||||
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: list = []
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: set[str] = set()
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: set[str] = set()
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: str
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: set[str] = []
|
||||
image: Image | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: str
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: set[str] = []
|
||||
image: Optional[Image] = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: set[str] = set()
|
||||
image: Image | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: set[str] = set()
|
||||
image: Optional[Image] = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: set[str] = set()
|
||||
images: list[Image] | None = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: set[str] = set()
|
||||
images: Optional[list[Image]] = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: set[str] = set()
|
||||
images: list[Image] | None = None
|
||||
|
||||
|
||||
class Offer(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
items: list[Item]
|
||||
|
||||
|
||||
@app.post("/offers/")
|
||||
async def create_offer(offer: Offer):
|
||||
return offer
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: set[str] = set()
|
||||
images: Optional[list[Image]] = None
|
||||
|
||||
|
||||
class Offer(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
items: list[Item]
|
||||
|
||||
|
||||
@app.post("/offers/")
|
||||
async def create_offer(offer: Offer):
|
||||
return offer
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
@app.post("/images/multiple/")
|
||||
async def create_multiple_images(images: list[Image]):
|
||||
return images
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/index-weights/")
|
||||
async def create_index_weights(weights: dict[int, float]):
|
||||
return weights
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str | None = None
|
||||
description: str | None = None
|
||||
price: float | None = None
|
||||
tax: float = 10.5
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.put("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
update_item_encoded = jsonable_encoder(item)
|
||||
items[item_id] = update_item_encoded
|
||||
return update_item_encoded
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
price: Optional[float] = None
|
||||
tax: float = 10.5
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.put("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
update_item_encoded = jsonable_encoder(item)
|
||||
items[item_id] = update_item_encoded
|
||||
return update_item_encoded
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str | None = None
|
||||
description: str | None = None
|
||||
price: float | None = None
|
||||
tax: float = 10.5
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.patch("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
stored_item_data = items[item_id]
|
||||
stored_item_model = Item(**stored_item_data)
|
||||
update_data = item.dict(exclude_unset=True)
|
||||
updated_item = stored_item_model.copy(update=update_data)
|
||||
items[item_id] = jsonable_encoder(updated_item)
|
||||
return updated_item
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
price: Optional[float] = None
|
||||
tax: float = 10.5
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.patch("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
stored_item_data = items[item_id]
|
||||
stored_item_model = Item(**stored_item_data)
|
||||
update_data = item.dict(exclude_unset=True)
|
||||
updated_item = stored_item_model.copy(update=update_data)
|
||||
items[item_id] = jsonable_encoder(updated_item)
|
||||
return updated_item
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from fastapi import Cookie, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(ads_id: str | None = Cookie(None)):
|
||||
return {"ads_id": ads_id}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
|
||||
return {"q": q, "skip": skip, "limit": limit}
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: dict = Depends(common_parameters)):
|
||||
return commons
|
||||
|
||||
|
||||
@app.get("/users/")
|
||||
async def read_users(commons: dict = Depends(common_parameters)):
|
||||
return commons
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str | None = 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(CommonQueryParams)):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
items = fake_items_db[commons.skip : commons.skip + commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
@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.skip + commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str | None = 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()):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
items = fake_items_db[commons.skip : commons.skip + commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from fastapi import Cookie, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def query_extractor(q: str | None = None):
|
||||
return q
|
||||
|
||||
|
||||
def query_or_cookie_extractor(
|
||||
q: str = Depends(query_extractor), last_query: str | None = 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}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from datetime import datetime
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
fake_db = {}
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
title: str
|
||||
timestamp: datetime
|
||||
description: str | None = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{id}")
|
||||
def update_item(id: str, item: Item):
|
||||
json_compatible_item_data = jsonable_encoder(item)
|
||||
fake_db[id] = json_compatible_item_data
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
from datetime import datetime, time, timedelta
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def read_items(
|
||||
item_id: UUID,
|
||||
start_datetime: datetime | None = Body(None),
|
||||
end_datetime: datetime | None = Body(None),
|
||||
repeat_at: time | None = Body(None),
|
||||
process_after: timedelta | None = Body(None),
|
||||
):
|
||||
start_process = start_datetime + process_after
|
||||
duration = end_datetime - start_process
|
||||
return {
|
||||
"item_id": item_id,
|
||||
"start_datetime": start_datetime,
|
||||
"end_datetime": end_datetime,
|
||||
"repeat_at": repeat_at,
|
||||
"process_after": process_after,
|
||||
"start_process": start_process,
|
||||
"duration": duration,
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class UserIn(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
email: EmailStr
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
class UserOut(BaseModel):
|
||||
username: str
|
||||
email: EmailStr
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
class UserInDB(BaseModel):
|
||||
username: str
|
||||
hashed_password: str
|
||||
email: EmailStr
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
def fake_password_hasher(raw_password: str):
|
||||
return "supersecret" + raw_password
|
||||
|
||||
|
||||
def fake_save_user(user_in: UserIn):
|
||||
hashed_password = fake_password_hasher(user_in.password)
|
||||
user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
|
||||
print("User saved! ..not really")
|
||||
return user_in_db
|
||||
|
||||
|
||||
@app.post("/user/", response_model=UserOut)
|
||||
async def create_user(user_in: UserIn):
|
||||
user_saved = fake_save_user(user_in)
|
||||
return user_saved
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
username: str
|
||||
email: EmailStr
|
||||
full_name: str | None = None
|
||||
|
||||
|
||||
class UserIn(UserBase):
|
||||
password: str
|
||||
|
||||
|
||||
class UserOut(UserBase):
|
||||
pass
|
||||
|
||||
|
||||
class UserInDB(UserBase):
|
||||
hashed_password: str
|
||||
|
||||
|
||||
def fake_password_hasher(raw_password: str):
|
||||
return "supersecret" + raw_password
|
||||
|
||||
|
||||
def fake_save_user(user_in: UserIn):
|
||||
hashed_password = fake_password_hasher(user_in.password)
|
||||
user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
|
||||
print("User saved! ..not really")
|
||||
return user_in_db
|
||||
|
||||
|
||||
@app.post("/user/", response_model=UserOut)
|
||||
async def create_user(user_in: UserIn):
|
||||
user_saved = fake_save_user(user_in)
|
||||
return user_saved
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class BaseItem(BaseModel):
|
||||
description: str
|
||||
type: str
|
||||
|
||||
|
||||
class CarItem(BaseItem):
|
||||
type = "car"
|
||||
|
||||
|
||||
class PlaneItem(BaseItem):
|
||||
type = "plane"
|
||||
size: int
|
||||
|
||||
|
||||
items = {
|
||||
"item1": {"description": "All my friends drive a low rider", "type": "car"},
|
||||
"item2": {
|
||||
"description": "Music is my aeroplane, it's my aeroplane",
|
||||
"type": "plane",
|
||||
"size": 5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
items = [
|
||||
{"name": "Foo", "description": "There comes my hero"},
|
||||
{"name": "Red", "description": "It's my aeroplane"},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/items/", response_model=list[Item])
|
||||
async def read_items():
|
||||
return items
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/keyword-weights/", response_model=dict[str, float])
|
||||
async def read_keyword_weights():
|
||||
return {"foo": 2.3, "bar": 3.4}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from fastapi import FastAPI, Header
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(user_agent: str | None = Header(None)):
|
||||
return {"User-Agent": user_agent}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from fastapi import FastAPI, Header
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(
|
||||
strange_header: str | None = Header(None, convert_underscores=False)
|
||||
):
|
||||
return {"strange_header": strange_header}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from fastapi import FastAPI, Header
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(x_token: list[str] | None = Header(None)):
|
||||
return {"X-Token values": x_token}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(x_token: Optional[list[str]] = Header(None)):
|
||||
return {"X-Token values": x_token}
|
||||
|
|
@ -11,7 +11,7 @@ class Item(BaseModel):
|
|||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: Set[str] = []
|
||||
tags: Set[str] = set()
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
from fastapi import FastAPI, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
price: float
|
||||
tax: float | None = None
|
||||
tags: set[str] = set()
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price: float
|
||||
tax: Optional[float] = None
|
||||
tags: set[str] = set()
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue