mirror of https://github.com/tiangolo/fastapi.git
Merge 1281fda2f7 into c48539f4c6
This commit is contained in:
commit
1d54c5cdb1
|
|
@ -55,6 +55,36 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
|
|||
}
|
||||
```
|
||||
|
||||
### Required fields that can be `None` { #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:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
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
|
||||
Loading…
Reference in New Issue