FastAPI Request Body Fields and Nested Models
Inside a Pydantic model, you can useFieldto declare validation rules and metadata for fields, and you can also nest models to handle complex JSON data structures.
Using Field to Declare Field Validation
Used in path operation functionsQuery、PathThe way to declare validation is the same; inside Pydantic models you useFieldDeclare field validation:
Example
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(min_length=1, max_length=100, description="Product Name") # Required, 1-100 characters
description: str | None = Field(default=None, max_length=300, description="Product Description") # Optional
price: float = Field(gt=0, description="Product Price") # Required, must be greater than 0
tax: float | None = Field(default=None, ge=0, description="Tax") # Optional, >= 0
@app.post("/items/")
async def create_item(item: Item):
return item
FieldSupported validation parameters:
| Parameter Type | Parameter | Description |
|---|---|---|
| String validation | min_length | Minimum length |
max_length | Maximum length | |
pattern | Regular expression matching | |
| Numeric validation | gt | greater than |
ge | greater than or equal to | |
lt | less than | |
le | less than or equal to | |
| metadata | title | Field title |
description | Field description |
Note
FieldfrompydanticImport, rather than fromfastapiImport. This isQuery、Pathetc. fromfastapiImport differs.
List type field
You can declare a field as a list and specify the element type:
Example
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = [] # String list, default empty list
@app.post("/items/")
async def create_item(item: Item):
return item
Request body example:
{
"name": "Foo",
"price": 42.0,
"tags": ["rock", "metal", "bar"]
}
Use Set to deduplicate
If tags should not be duplicated, usesetType:
Example
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set() # Automatically remove duplicate tags
Even if the request contains duplicate tags, they will be automatically deduplicated in the response.
Nested model
A Pydantic model's field type can be another Pydantic model, supporting nesting to any depth:
Example
from pydantic import BaseModel, HttpUrl
app = FastAPI()
# Sub-model: Image
class Image(BaseModel):
url: HttpUrl # Automatically validate whether it is a valid URL
name: str
# Main model: Product, includes image submodel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None # Optional image information
@app.post("/items/")
async def create_item(item: Item):
return item
Corresponding request body structure:
{
"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"
}
}
Submodel in a list
You can put sub-models in a list:
Example
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 # Image List
@app.post("/items/")
async def create_item(item: Item):
return item
Corresponding request body:
{
"name": "Foo",
"price": 42.0,
"images": [
{"url": "http://example.com/baz.jpg", "name": "The Foo live"},
{"url": "http://example.com/dave.jpg", "name": "The Baz"}
]
}
Deeply nested models
You can define nested structures of any depth:
Example
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] # Offer contains multiple Items, and an Item contains multiple Images
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
Pure list request body
If the outermost layer of the request body is a JSON array, you can directly declare a list type in the function parameter:
Example
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
# Request body is directly a list of Images
@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
return images
Request body composed of an arbitrary dict
When you need to receive dictionary data with uncertain key names, you can declaredictRequest body of type:
Example
app = FastAPI()
# Receive a dict with int keys and float values
@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
return weights
Request body example:
{
"1": 0.5,
"2": 1.5,
"3": 2.0
}
JSON only supports string-type keys, but Pydantic will automatically convert string keys to the declared type (such as the
int)。
Summary
- Using Pydantic's
FieldAdd validation and metadata to model fields list[str]、set[str]Collection types such as these can be declared as list/set fields- Models can be nested, supporting JSON structures of any depth
HttpUrland other special types automatically validate the format- Plain lists and arbitrary dictionaries can also be used as request body types