]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
✨ Support `dataclasses` in responses (#3576)
authorSebastián Ramírez <tiangolo@gmail.com>
Wed, 21 Jul 2021 14:39:12 +0000 (16:39 +0200)
committerGitHub <noreply@github.com>
Wed, 21 Jul 2021 14:39:12 +0000 (14:39 +0000)
Co-authored-by: amit lissack <amit@opentrons.com>
fastapi/encoders.py
fastapi/routing.py
tests/test_serialize_response_dataclass.py

index 51cab419d4944b74f8b1ad602b799dcc19720201..3f599c9faa04533ddf29af92a3de9da7caf2ac7f 100644 (file)
@@ -1,3 +1,4 @@
+import dataclasses
 from collections import defaultdict
 from enum import Enum
 from pathlib import PurePath
@@ -61,6 +62,8 @@ def jsonable_encoder(
             custom_encoder=encoder,
             sqlalchemy_safe=sqlalchemy_safe,
         )
+    if dataclasses.is_dataclass(obj):
+        return dataclasses.asdict(obj)
     if isinstance(obj, Enum):
         return obj.value
     if isinstance(obj, PurePath):
index a7c62af3cbb2b8a8c1699766959b2f600a876885..f5fe0c0bde0a30d40275088a5b553479def05b29 100644 (file)
@@ -1,4 +1,5 @@
 import asyncio
+import dataclasses
 import email.message
 import enum
 import inspect
@@ -90,6 +91,8 @@ def _prepare_response_content(
             )
             for k, v in res.items()
         }
+    elif dataclasses.is_dataclass(res):
+        return dataclasses.asdict(res)
     return res
 
 
index d1b64c4e8ef3ab105232db2660d14d78cee8a019..e520338ec68f0aff23aba7a29daf790ad46a39e1 100644 (file)
@@ -19,6 +19,11 @@ def get_valid():
     return {"name": "valid", "price": 1.0}
 
 
+@app.get("/items/object", response_model=Item)
+def get_object():
+    return Item(name="object", price=1.0, owner_ids=[1, 2, 3])
+
+
 @app.get("/items/coerce", response_model=Item)
 def get_coerce():
     return {"name": "coerce", "price": "1.0"}
@@ -33,6 +38,29 @@ def get_validlist():
     ]
 
 
+@app.get("/items/objectlist", response_model=List[Item])
+def get_objectlist():
+    return [
+        Item(name="foo"),
+        Item(name="bar", price=1.0),
+        Item(name="baz", price=2.0, owner_ids=[1, 2, 3]),
+    ]
+
+
+@app.get("/items/no-response-model/object")
+def get_no_response_model_object():
+    return Item(name="object", price=1.0, owner_ids=[1, 2, 3])
+
+
+@app.get("/items/no-response-model/objectlist")
+def get_no_response_model_objectlist():
+    return [
+        Item(name="foo"),
+        Item(name="bar", price=1.0),
+        Item(name="baz", price=2.0, owner_ids=[1, 2, 3]),
+    ]
+
+
 client = TestClient(app)
 
 
@@ -42,6 +70,12 @@ def test_valid():
     assert response.json() == {"name": "valid", "price": 1.0, "owner_ids": None}
 
 
+def test_object():
+    response = client.get("/items/object")
+    response.raise_for_status()
+    assert response.json() == {"name": "object", "price": 1.0, "owner_ids": [1, 2, 3]}
+
+
 def test_coerce():
     response = client.get("/items/coerce")
     response.raise_for_status()
@@ -56,3 +90,29 @@ def test_validlist():
         {"name": "bar", "price": 1.0, "owner_ids": None},
         {"name": "baz", "price": 2.0, "owner_ids": [1, 2, 3]},
     ]
+
+
+def test_objectlist():
+    response = client.get("/items/objectlist")
+    response.raise_for_status()
+    assert response.json() == [
+        {"name": "foo", "price": None, "owner_ids": None},
+        {"name": "bar", "price": 1.0, "owner_ids": None},
+        {"name": "baz", "price": 2.0, "owner_ids": [1, 2, 3]},
+    ]
+
+
+def test_no_response_model_object():
+    response = client.get("/items/no-response-model/object")
+    response.raise_for_status()
+    assert response.json() == {"name": "object", "price": 1.0, "owner_ids": [1, 2, 3]}
+
+
+def test_no_response_model_objectlist():
+    response = client.get("/items/no-response-model/objectlist")
+    response.raise_for_status()
+    assert response.json() == [
+        {"name": "foo", "price": None, "owner_ids": None},
+        {"name": "bar", "price": 1.0, "owner_ids": None},
+        {"name": "baz", "price": 2.0, "owner_ids": [1, 2, 3]},
+    ]