mirror of https://github.com/tiangolo/fastapi.git
Merge fc648a866b into cc6ced6345
This commit is contained in:
commit
c904b05dd1
|
|
@ -246,11 +246,22 @@ So, when you need to declare a value as required while using `Query`, you can si
|
|||
|
||||
### Required, can be `None` { #required-can-be-none }
|
||||
|
||||
You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`.
|
||||
You might want to declare a parameter that can accept `None` but is still required.
|
||||
|
||||
To do that, you can declare that `None` is a valid type but simply do not declare a default value:
|
||||
However, because of how **HTTP query parameters** work, clients can not actually send a real `None` (or `null`) value - query parameters are always sent as **strings**.
|
||||
That means you cannot truly have a *required* parameter that also allows a real `None` value.
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
|
||||
For example, you might try:
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None, Query(min_length=3)] = ...
|
||||
```
|
||||
|
||||
But this will still expect a **string** value, and if the client tries to send `q=None`, `q=null` or `q=`, these values will be treated by FastAPI as strings `"None"`, `"null"` and `""` (empty string) respectively.
|
||||
|
||||
If you want to accept special values (like `"None"` or an empty string) and interpret them as `None` in your application, you can handle them manually in your function:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9:12,17] *}
|
||||
|
||||
## Query parameter list / multiple values { #query-parameter-list-multiple-values }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
from pydantic import BeforeValidator
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def nullable_str(val: str) -> str | None:
|
||||
if val in ("None", "", "null"):
|
||||
return None
|
||||
return val
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[str | None, Query(min_length=3)]):
|
||||
async def read_items(
|
||||
q: Annotated[str | None, Query(min_length=3), BeforeValidator(nullable_str)],
|
||||
):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
from pydantic import BeforeValidator
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def nullable_str(val: str) -> Union[str, None]:
|
||||
if val in ("None", "", "null"):
|
||||
return None
|
||||
return val
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]):
|
||||
async def read_items(
|
||||
q: Annotated[Union[str, None], Query(min_length=3), BeforeValidator(nullable_str)],
|
||||
):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: str | None = Query(min_length=3)):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Union[str, None] = Query(min_length=3)):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
|
|
@ -9,8 +9,6 @@ from ...utils import needs_py310
|
|||
@pytest.fixture(
|
||||
name="client",
|
||||
params=[
|
||||
pytest.param("tutorial006c_py39"),
|
||||
pytest.param("tutorial006c_py310", marks=needs_py310),
|
||||
pytest.param("tutorial006c_an_py39"),
|
||||
pytest.param("tutorial006c_an_py310", marks=needs_py310),
|
||||
],
|
||||
|
|
@ -23,24 +21,28 @@ def get_client(request: pytest.FixtureRequest):
|
|||
return client
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
reason="Code example is not valid. See https://github.com/fastapi/fastapi/issues/12419"
|
||||
)
|
||||
def test_query_params_str_validations_no_query(client: TestClient):
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == { # pragma: no cover
|
||||
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
|
||||
assert response.status_code == 422
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": ["query", "q"],
|
||||
"msg": "Field required",
|
||||
"input": None,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
reason="Code example is not valid. See https://github.com/fastapi/fastapi/issues/12419"
|
||||
)
|
||||
def test_query_params_str_validations_empty_str(client: TestClient):
|
||||
response = client.get("/items/?q=")
|
||||
@pytest.mark.parametrize("q_value", ["None", "null", ""])
|
||||
def test_query_params_str_validations_send_explicit_none(
|
||||
client: TestClient, q_value: str
|
||||
):
|
||||
response = client.get("/items/", params={"q": q_value})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == { # pragma: no cover
|
||||
assert response.json() == {
|
||||
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue