mirror of https://github.com/tiangolo/fastapi.git
🐛 Fix `jsonable_encoder` for dataclasses with pydantic-compatible fields (#3607)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
de6ccd7754
commit
22bed0008c
|
|
@ -71,7 +71,14 @@ def jsonable_encoder(
|
|||
sqlalchemy_safe=sqlalchemy_safe,
|
||||
)
|
||||
if dataclasses.is_dataclass(obj):
|
||||
return dataclasses.asdict(obj)
|
||||
obj_dict = dataclasses.asdict(obj)
|
||||
return jsonable_encoder(
|
||||
obj_dict,
|
||||
exclude_none=exclude_none,
|
||||
exclude_defaults=exclude_defaults,
|
||||
custom_encoder=custom_encoder,
|
||||
sqlalchemy_safe=sqlalchemy_safe,
|
||||
)
|
||||
if isinstance(obj, Enum):
|
||||
return obj.value
|
||||
if isinstance(obj, PurePath):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic.dataclasses import dataclass
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
|
@ -10,54 +11,64 @@ app = FastAPI()
|
|||
@dataclass
|
||||
class Item:
|
||||
name: str
|
||||
date: datetime
|
||||
price: Optional[float] = None
|
||||
owner_ids: Optional[List[int]] = None
|
||||
|
||||
|
||||
@app.get("/items/valid", response_model=Item)
|
||||
def get_valid():
|
||||
return {"name": "valid", "price": 1.0}
|
||||
return {"name": "valid", "date": datetime(2021, 7, 26), "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])
|
||||
return Item(
|
||||
name="object", date=datetime(2021, 7, 26), price=1.0, owner_ids=[1, 2, 3]
|
||||
)
|
||||
|
||||
|
||||
@app.get("/items/coerce", response_model=Item)
|
||||
def get_coerce():
|
||||
return {"name": "coerce", "price": "1.0"}
|
||||
return {"name": "coerce", "date": datetime(2021, 7, 26).isoformat(), "price": "1.0"}
|
||||
|
||||
|
||||
@app.get("/items/validlist", response_model=List[Item])
|
||||
def get_validlist():
|
||||
return [
|
||||
{"name": "foo"},
|
||||
{"name": "bar", "price": 1.0},
|
||||
{"name": "baz", "price": 2.0, "owner_ids": [1, 2, 3]},
|
||||
{"name": "foo", "date": datetime(2021, 7, 26)},
|
||||
{"name": "bar", "date": datetime(2021, 7, 26), "price": 1.0},
|
||||
{
|
||||
"name": "baz",
|
||||
"date": datetime(2021, 7, 26),
|
||||
"price": 2.0,
|
||||
"owner_ids": [1, 2, 3],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@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]),
|
||||
Item(name="foo", date=datetime(2021, 7, 26)),
|
||||
Item(name="bar", date=datetime(2021, 7, 26), price=1.0),
|
||||
Item(name="baz", date=datetime(2021, 7, 26), 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])
|
||||
return Item(
|
||||
name="object", date=datetime(2021, 7, 26), 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]),
|
||||
Item(name="foo", date=datetime(2021, 7, 26)),
|
||||
Item(name="bar", date=datetime(2021, 7, 26), price=1.0),
|
||||
Item(name="baz", date=datetime(2021, 7, 26), price=2.0, owner_ids=[1, 2, 3]),
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -67,28 +78,58 @@ client = TestClient(app)
|
|||
def test_valid():
|
||||
response = client.get("/items/valid")
|
||||
response.raise_for_status()
|
||||
assert response.json() == {"name": "valid", "price": 1.0, "owner_ids": None}
|
||||
assert response.json() == {
|
||||
"name": "valid",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"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]}
|
||||
assert response.json() == {
|
||||
"name": "object",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 1.0,
|
||||
"owner_ids": [1, 2, 3],
|
||||
}
|
||||
|
||||
|
||||
def test_coerce():
|
||||
response = client.get("/items/coerce")
|
||||
response.raise_for_status()
|
||||
assert response.json() == {"name": "coerce", "price": 1.0, "owner_ids": None}
|
||||
assert response.json() == {
|
||||
"name": "coerce",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 1.0,
|
||||
"owner_ids": None,
|
||||
}
|
||||
|
||||
|
||||
def test_validlist():
|
||||
response = client.get("/items/validlist")
|
||||
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]},
|
||||
{
|
||||
"name": "foo",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": None,
|
||||
"owner_ids": None,
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 1.0,
|
||||
"owner_ids": None,
|
||||
},
|
||||
{
|
||||
"name": "baz",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 2.0,
|
||||
"owner_ids": [1, 2, 3],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -96,23 +137,58 @@ 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]},
|
||||
{
|
||||
"name": "foo",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": None,
|
||||
"owner_ids": None,
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 1.0,
|
||||
"owner_ids": None,
|
||||
},
|
||||
{
|
||||
"name": "baz",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"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]}
|
||||
assert response.json() == {
|
||||
"name": "object",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"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]},
|
||||
{
|
||||
"name": "foo",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": None,
|
||||
"owner_ids": None,
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 1.0,
|
||||
"owner_ids": None,
|
||||
},
|
||||
{
|
||||
"name": "baz",
|
||||
"date": datetime(2021, 7, 26).isoformat(),
|
||||
"price": 2.0,
|
||||
"owner_ids": [1, 2, 3],
|
||||
},
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue