]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
🐛 Fix support for query parameters with list types, handle JSON encoding Pydantic...
authorarjwilliams <arjwilliams@yahoo.co.uk>
Thu, 18 Apr 2024 21:56:59 +0000 (22:56 +0100)
committerGitHub <noreply@github.com>
Thu, 18 Apr 2024 21:56:59 +0000 (16:56 -0500)
Co-authored-by: Andrew Williams <Andrew.Williams@contemi.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
fastapi/encoders.py
tests/main.py
tests/test_application.py
tests/test_jsonable_encoder.py
tests/test_query.py

index 2f9c4a4f7ca76692949a31607831671f8b4de8b7..451ea0760f07bc2a70029b99b124883b42a92f9c 100644 (file)
@@ -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())
index 15760c0396941e1be12c2274012910aebdffc466..6927eab61b254b389bdf098eb0d6adec789f9046 100644 (file)
@@ -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
index ea7a80128fd68b3b8b835690a94cd59cf954cb30..5c62f5f6e2a013cb2033e6862108e50f70c72de8 100644 (file)
@@ -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": {
index 7c8338ff376d04f701c50031827d90330f4f8729..1906d6bf17c7eb163f717363e1dc47f69b9ac2cf 100644 (file)
@@ -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}
index 2ce4fcd0b16888fb2c48f661b0d7df8211050a3a..57f551d2ab22c22c1d8526e1d7d8070e09931616 100644 (file)
@@ -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() == []