mirror of https://github.com/tiangolo/fastapi.git
214 lines
5.5 KiB
Markdown
214 lines
5.5 KiB
Markdown
With **FastAPI**, you can define, validate, document, and use arbitrarily deeply nested models (thanks to Pydantic).
|
|
|
|
## List fields
|
|
|
|
You can define an attribute to be a subtype. For example, a Python `list`:
|
|
|
|
```Python hl_lines="12"
|
|
{!./src/body_nested_models/tutorial001.py!}
|
|
```
|
|
|
|
This will make `tags` be a list of items. Although it doesn't declare the type of each of the items.
|
|
|
|
## List fields with subtype
|
|
|
|
But Python has a specific way to declare lists with subtypes:
|
|
|
|
### Import typing's `List`
|
|
|
|
First, import `List` from standard Python's `typing` module:
|
|
|
|
```Python hl_lines="1"
|
|
{!./src/body_nested_models/tutorial002.py!}
|
|
```
|
|
|
|
### Declare a `List` with a subtype
|
|
|
|
To declare types that have subtypes, like `list`, `dict`, `tuple`:
|
|
|
|
* Import them from the `typing` module
|
|
* Pass the subtype(s) as "type arguments" using square brackets: `[` and `]`
|
|
|
|
```Python
|
|
from typing import List
|
|
|
|
my_list: List[str]
|
|
```
|
|
|
|
That's all standard Python syntax for type declarations.
|
|
|
|
Use that same standard syntax for model attributes with subtypes.
|
|
|
|
So, in our example, we can make `tags` be specifically a "list of strings":
|
|
|
|
```Python hl_lines="14"
|
|
{!./src/body_nested_models/tutorial002.py!}
|
|
```
|
|
|
|
## Set types
|
|
|
|
But then we think about it, and realize that tags shouldn't repeat, they would probably be unique strings.
|
|
|
|
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`:
|
|
|
|
```Python hl_lines="1 14"
|
|
{!./src/body_nested_models/tutorial003.py!}
|
|
```
|
|
|
|
With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
|
|
|
|
And whenever you output that data, even if the source had duplicates, it will be output as a set of unique items.
|
|
|
|
And it will be annotated / documented accordingly too.
|
|
|
|
## Nested Models
|
|
|
|
Each attribute of a Pydantic model has a type.
|
|
|
|
But that type can itself be another Pydantic model.
|
|
|
|
So, you can declare deeply nested JSON `object`s with specific attribute names, types and validations.
|
|
|
|
All that, arbitrarily nested.
|
|
|
|
### Define a submodel
|
|
|
|
For example, we can define an `Image` model:
|
|
|
|
```Python hl_lines="9 10 11"
|
|
{!./src/body_nested_models/tutorial004.py!}
|
|
```
|
|
|
|
### Use the submodel as a type
|
|
|
|
And then we can use it as the type of an attribute:
|
|
|
|
```Python hl_lines="20"
|
|
{!./src/body_nested_models/tutorial004.py!}
|
|
```
|
|
|
|
This would mean that **FastAPI** would expect a body similar to:
|
|
|
|
```JSON
|
|
{
|
|
"name": "Foo",
|
|
"description": "The pretender",
|
|
"price": 42.0,
|
|
"tax": 3.2,
|
|
"tags": ["rock", "metal", "bar"],
|
|
"image": {
|
|
"url": "http://example.com/baz.jpg",
|
|
"name": "The Foo live"
|
|
}
|
|
}
|
|
```
|
|
|
|
Again, doing just that declaration, with **FastAPI** you get:
|
|
|
|
* Editor support (completion, etc), even for nested models
|
|
* Data conversion
|
|
* Data validation
|
|
* Automatic documentation
|
|
|
|
## Special types and validation
|
|
|
|
Apart from normal singular types like `str`, `int`, `float`, etc. You can use more complex singular types that inherit from `str`.
|
|
|
|
To see all the options you have, checkout the docs for <a href="https://pydantic-docs.helpmanual.io/#exotic-types" target="_blank">Pydantic's exotic types</a>. You will see some examples in the next chapter.
|
|
|
|
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 `UrlStr`:
|
|
|
|
```Python hl_lines="4 10"
|
|
{!./src/body_nested_models/tutorial005.py!}
|
|
```
|
|
|
|
The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
|
|
|
|
## Attributes with lists of submodels
|
|
|
|
You can also use Pydantic models as subtypes of `list`, `set`, etc:
|
|
|
|
```Python hl_lines="20"
|
|
{!./src/body_nested_models/tutorial006.py!}
|
|
```
|
|
|
|
This will expect (convert, validate, document, etc) a JSON body like:
|
|
|
|
```JSON hl_lines="11"
|
|
{
|
|
"name": "Foo",
|
|
"description": "The pretender",
|
|
"price": 42.0,
|
|
"tax": 3.2,
|
|
"tags": [
|
|
"rock",
|
|
"metal",
|
|
"bar"
|
|
],
|
|
"images": [
|
|
{
|
|
"url": "http://example.com/baz.jpg",
|
|
"name": "The Foo live"
|
|
},
|
|
{
|
|
"url": "http://example.com/dave.jpg",
|
|
"name": "The Baz"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
!!! info
|
|
Notice how the `images` key now has a list of image objects.
|
|
|
|
## Deeply nested models
|
|
|
|
You can define arbitrarily deeply nested models:
|
|
|
|
```Python hl_lines="9 14 20 23 27"
|
|
{!./src/body_nested_models/tutorial007.py!}
|
|
```
|
|
|
|
!!! info
|
|
Notice how `Offer` as a list of `Item`s, which in turn have an optional list of `Image`s
|
|
|
|
## Bodies of pure lists
|
|
|
|
If the top level value of the JSON body you expect is a JSON `array` (a Python `list`), you can declare the type in the parameter of the function, the same as in Pydantic models:
|
|
|
|
```Python
|
|
images: List[Image]
|
|
```
|
|
|
|
as in:
|
|
|
|
```Python hl_lines="15"
|
|
{!./src/body_nested_models/tutorial008.py!}
|
|
```
|
|
|
|
## Editor support everywhere
|
|
|
|
And you get editor support everywhere.
|
|
|
|
Even for items inside of lists:
|
|
|
|
<img src="/img/tutorial/body-nested-models/image01.png">
|
|
|
|
You couldn't get this kind of editor support if you where working directly with `dict` instead of Pydantic models.
|
|
|
|
But you don't have to worry about them either, incoming dicts are converted automatically and your output is converted automatically to JSON too.
|
|
|
|
## Recap
|
|
|
|
With **FastAPI** you have the maximum flexibility provided by Pydantic models, while keeping your code simple, short and elegant.
|
|
|
|
But with all the benefits:
|
|
|
|
* Editor support (completion everywhere!)
|
|
* Data conversion (a.k.a. parsing / serialization)
|
|
* Data validation
|
|
* Schema documentation
|
|
* Automatic docs
|