mirror of https://github.com/tiangolo/fastapi.git
🐛 Fix support for query parameters with list types, handle JSON encoding Pydantic `UndefinedType` (#9929)
Co-authored-by: Andrew Williams <Andrew.Williams@contemi.com> Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
This commit is contained in:
parent
071b8f27f9
commit
09e4859cab
|
|
@ -24,7 +24,7 @@ from pydantic.networks import AnyUrl, NameEmail
|
|||
from pydantic.types import SecretBytes, SecretStr
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
from ._compat import PYDANTIC_V2, Url, _model_dump
|
||||
from ._compat import PYDANTIC_V2, UndefinedType, Url, _model_dump
|
||||
|
||||
|
||||
# Taken from Pydantic v1 as is
|
||||
|
|
@ -259,6 +259,8 @@ def jsonable_encoder(
|
|||
return str(obj)
|
||||
if isinstance(obj, (str, int, float, type(None))):
|
||||
return obj
|
||||
if isinstance(obj, UndefinedType):
|
||||
return None
|
||||
if isinstance(obj, dict):
|
||||
encoded_dict = {}
|
||||
allowed_keys = set(obj.keys())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import http
|
||||
from typing import FrozenSet, Optional
|
||||
from typing import FrozenSet, List, Optional
|
||||
|
||||
from fastapi import FastAPI, Path, Query
|
||||
|
||||
|
|
@ -192,3 +192,13 @@ def get_enum_status_code():
|
|||
@app.get("/query/frozenset")
|
||||
def get_query_type_frozenset(query: FrozenSet[int] = Query(...)):
|
||||
return ",".join(map(str, sorted(query)))
|
||||
|
||||
|
||||
@app.get("/query/list")
|
||||
def get_query_list(device_ids: List[int] = Query()) -> List[int]:
|
||||
return device_ids
|
||||
|
||||
|
||||
@app.get("/query/list-default")
|
||||
def get_query_list_default(device_ids: List[int] = Query(default=[])) -> List[int]:
|
||||
return device_ids
|
||||
|
|
|
|||
|
|
@ -1163,6 +1163,91 @@ def test_openapi_schema():
|
|||
},
|
||||
}
|
||||
},
|
||||
"/query/list": {
|
||||
"get": {
|
||||
"summary": "Get Query List",
|
||||
"operationId": "get_query_list_query_list_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "device_ids",
|
||||
"in": "query",
|
||||
"required": True,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"title": "Device Ids",
|
||||
},
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"title": "Response Get Query List Query List Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"/query/list-default": {
|
||||
"get": {
|
||||
"summary": "Get Query List Default",
|
||||
"operationId": "get_query_list_default_query_list_default_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "device_ids",
|
||||
"in": "query",
|
||||
"required": False,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"default": [],
|
||||
"title": "Device Ids",
|
||||
},
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"title": "Response Get Query List Default Query List Default Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from pathlib import PurePath, PurePosixPath, PureWindowsPath
|
|||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from fastapi._compat import PYDANTIC_V2
|
||||
from fastapi._compat import PYDANTIC_V2, Undefined
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
|
||||
|
|
@ -310,3 +310,9 @@ def test_encode_deque_encodes_child_models():
|
|||
dq = deque([Model(test="test")])
|
||||
|
||||
assert jsonable_encoder(dq)[0]["test"] == "test"
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_encode_pydantic_undefined():
|
||||
data = {"value": Undefined}
|
||||
assert jsonable_encoder(data) == {"value": None}
|
||||
|
|
|
|||
|
|
@ -396,3 +396,26 @@ def test_query_frozenset_query_1_query_1_query_2():
|
|||
response = client.get("/query/frozenset/?query=1&query=1&query=2")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == "1,2"
|
||||
|
||||
|
||||
def test_query_list():
|
||||
response = client.get("/query/list/?device_ids=1&device_ids=2")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [1, 2]
|
||||
|
||||
|
||||
def test_query_list_empty():
|
||||
response = client.get("/query/list/")
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_query_list_default():
|
||||
response = client.get("/query/list-default/?device_ids=1&device_ids=2")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [1, 2]
|
||||
|
||||
|
||||
def test_query_list_default_empty():
|
||||
response = client.get("/query/list-default/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == []
|
||||
|
|
|
|||
Loading…
Reference in New Issue