docs: clarify required but nullable request body fields

This commit is contained in:
Alberto Zambrano 2026-01-21 13:41:52 +01:00
parent e0abd210f6
commit eec2fe9033
5 changed files with 81 additions and 0 deletions

View File

@ -55,6 +55,36 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
}
```
### Required fields that can be `None`
In Python type hints, a parameter can be **required** and still allow the value `None`.
This means that the field must be present in the request body, but its value can be `null`
(`None` in Python).
This typically happens when you use `Optional[T]` **without** providing a default value.
For example:
{* ../../docs_src/body/tutorial005_py39.py hl[6] *}
And for Python 3.10+:
{* ../../docs_src/body/tutorial005_py310.py hl[6] *}
In this example:
* The `description` field is **required** (the client must include it in the JSON body).
* Its value can still be `null` (`None` in Python).
* This is different from a truly optional field, which would have a default value:
```python
class Item(BaseModel):
description: Optional[str] = None
```
Here, `description` can be omitted entirely in the request body.
## Declare it as a parameter { #declare-it-as-a-parameter }
To add it to your *path operation*, declare it the same way you declared path and query parameters:

View File

@ -0,0 +1,11 @@
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
description: str | None
@app.post("/items/")
async def create_item(item: Item):
return item

View File

@ -0,0 +1,12 @@
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
description: Optional[str]
@app.post("/items/")
async def create_item(item: Item):
return item

View File

@ -0,0 +1,28 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body.{request.param}")
client = TestClient(mod.app)
return client
def test_required_nullable_field(client: TestClient):
response = client.post("/items/", json={"description": None})
assert response.status_code == 200
assert response.json() == {"description": None}
def test_required_field_missing(client: TestClient):
response = client.post("/items/", json={})
assert response.status_code == 422