Body - Múltiples Parámetros¶
Ahora que hemos visto cómo usar Path y Query, veamos usos más avanzados de las declaraciones de cuerpo de la petición (request body).
Mezclar parámetros Path, Query y body¶
Primero, por supuesto, puedes mezclar libremente declaraciones de parámetros Path, Query y cuerpo de la petición y FastAPI sabrá qué hacer.
Y también puedes declarar parámetros de body como opcionales, estableciendo el valor por defecto a None:
from typing import Annotated
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: Annotated[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
🤓 Otras versiones y variantes
Consejo
Preferible usar la versión con Annotated si es posible.
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
Nota
Nota que, en este caso, el item que se tomaría del body es opcional. Ya que tiene un valor por defecto de None.
Múltiples parámetros de body¶
En el ejemplo anterior, las path operations esperarían un body JSON con los atributos de un Item, como:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
Pero también puedes declarar múltiples parámetros de body, por ejemplo item y user:
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
En este caso, FastAPI notará que hay más de un parámetro de body en la función (hay dos parámetros que son modelos Pydantic).
Así, usará los nombres de los parámetros como claves (nombres de campos) en el body, y esperará un body como:
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
Nota
Nota que aunque el item fue declarado de la misma manera que antes, ahora se espera que esté dentro del body con una clave item.
FastAPI hará la conversión automática desde la petición, de manera que el parámetro item reciba su contenido específico y lo mismo para user.
Realizará la validación de los datos compuestos, y lo documentará así para el esquema OpenAPI y la documentación automática.
Valores singulares en el body¶
De la misma manera que existe Query y Path para definir datos extra para los parámetros de query y path, FastAPI proporciona un Body equivalente.
Por ejemplo, extendiendo el modelo anterior, podrías decidir que quieres tener otra clave importance en el mismo body, además del item y user.
Si lo declaras tal cual, como es un valor singular, FastAPI asumirá que es un parámetro de query.
Pero puedes indicarle a FastAPI que lo trate como otra clave del body usando Body:
from typing import Annotated
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: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
🤓 Otras versiones y variantes
Consejo
Preferible usar la versión con Annotated si es posible.
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
En este caso, FastAPI esperará un body como:
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
De nuevo, convertirá los tipos de datos, validará, documentará, etc.
Múltiples parámetros de body y query¶
Por supuesto, también puedes declarar parámetros de query adicionales cuando lo necesites, además de cualquier parámetro de body.
Como, por defecto, los valores singulares se interpretan como parámetros de query, no tienes que añadir explícitamente un Query, puedes simplemente hacer:
q: str | None = None
Por ejemplo:
from typing import Annotated
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: Annotated[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
🤓 Otras versiones y variantes
Consejo
Preferible usar la versión con Annotated si es posible.
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
Nota
Body también tiene todos los mismos parámetros extra de validación y metadatos que Query, Path y otros que verás más adelante.
Embeber un único parámetro de body¶
Digamos que solo tienes un único parámetro de body item de un modelo Pydantic Item.
Por defecto, FastAPI esperará su body directamente.
Pero si quieres que espere un JSON con una clave item y dentro de ella los contenidos del modelo, como hace cuando declaras parámetros de body extra, puedes usar el parámetro especial de Body embed:
item: Annotated[Item, Body(embed=True)]
como en:
from typing import Annotated
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: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
🤓 Otras versiones y variantes
Consejo
Preferible usar la versión con Annotated si es posible.
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
En este caso FastAPI esperará un body como:
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
en lugar de:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
Resumen¶
Puedes añadir múltiples parámetros de body a tu función de path operation, aunque una petición solo puede tener un único body.
Pero FastAPI lo manejará, te dará los datos correctos en tu función, y validará y documentará el esquema correcto en la path operation.
También puedes declarar valores singulares para ser recibidos como parte del body.
Y puedes indicarle a FastAPI que embeba el body en una clave incluso cuando solo hay un único parámetro declarado.