Browse Source

📝 Add tutorial for body nested models

pull/11/head
Sebastián Ramírez 6 years ago
parent
commit
76f2b67d38
  1. BIN
      docs/img/tutorial/body-nested-models/image01.png
  2. 213
      docs/tutorial/body-nested-models.md
  3. 18
      docs/tutorial/src/body-nested-models/tutorial001.py
  4. 20
      docs/tutorial/src/body-nested-models/tutorial002.py
  5. 20
      docs/tutorial/src/body-nested-models/tutorial003.py
  6. 26
      docs/tutorial/src/body-nested-models/tutorial004.py
  7. 27
      docs/tutorial/src/body-nested-models/tutorial005.py
  8. 27
      docs/tutorial/src/body-nested-models/tutorial006.py
  9. 33
      docs/tutorial/src/body-nested-models/tutorial007.py
  10. 17
      docs/tutorial/src/body-nested-models/tutorial008.py
  11. 1
      mkdocs.yml

BIN
docs/img/tutorial/body-nested-models/image01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

213
docs/tutorial/body-nested-models.md

@ -0,0 +1,213 @@
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"
{!./tutorial/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"
{!./tutorial/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"
{!./tutorial/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"
{!./tutorial/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"
{!./tutorial/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"
{!./tutorial/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>.
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="5 11"
{!./tutorial/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="21"
{!./tutorial/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="10 15 21 24 28"
{!./tutorial/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="16"
{!./tutorial/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

18
docs/tutorial/src/body-nested-models/tutorial001.py

@ -0,0 +1,18 @@
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = 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

20
docs/tutorial/src/body-nested-models/tutorial002.py

@ -0,0 +1,20 @@
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: 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

20
docs/tutorial/src/body-nested-models/tutorial003.py

@ -0,0 +1,20 @@
from typing import Set
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: 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

26
docs/tutorial/src/body-nested-models/tutorial004.py

@ -0,0 +1,26 @@
from typing import Set
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
price: float
tax: float = None
tags: Set[str] = []
image: 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

27
docs/tutorial/src/body-nested-models/tutorial005.py

@ -0,0 +1,27 @@
from typing import Set
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
app = FastAPI()
class Image(BaseModel):
url: UrlStr
name: str
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
tags: Set[str] = []
image: 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

27
docs/tutorial/src/body-nested-models/tutorial006.py

@ -0,0 +1,27 @@
from typing import List, Set
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
app = FastAPI()
class Image(BaseModel):
url: UrlStr
name: str
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
tags: Set[str] = []
image: 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

33
docs/tutorial/src/body-nested-models/tutorial007.py

@ -0,0 +1,33 @@
from typing import List, Set
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
app = FastAPI()
class Image(BaseModel):
url: UrlStr
name: str
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
tags: Set[str] = []
image: List[Image] = None
class Offer(BaseModel):
name: str
description: str = None
price: float
items: List[Item]
@app.post("/offers/")
async def create_offer(*, offer: Offer):
return offer

17
docs/tutorial/src/body-nested-models/tutorial008.py

@ -0,0 +1,17 @@
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
app = FastAPI()
class Image(BaseModel):
url: UrlStr
name: str
@app.post("/images/multiple/")
async def create_multiple_images(*, images: List[Image]):
return images

1
mkdocs.yml

@ -26,6 +26,7 @@ nav:
- Path Parameters - Numeric Validations: 'tutorial/path-params-numeric-validations.md'
- Body - Multiple Parameters: 'tutorial/body-multiple-params.md'
- Body - Schema: 'tutorial/body-schema.md'
- Body - Nested Models: 'tutorial/body-nested-models.md'
- Concurrency and async / await: 'async.md'
- Deployment: 'deployment.md'

Loading…
Cancel
Save