Skip to content

Commit 9f55f4c

Browse files
authored
Merge pull request #8 from boydgreenfield/boyd/pydantic-alias-support
Add support for Pydantic models with `alias=`
2 parents e9bf5ab + 75f72c6 commit 9f55f4c

File tree

2 files changed

+15
-6
lines changed

2 files changed

+15
-6
lines changed

fastopenapi/base_router.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ def _build_parameters_and_body(
225225
param.annotation, BaseModel
226226
):
227227
if http_method.upper() == "GET":
228-
model_schema = param.annotation.model_json_schema()
228+
model_schema = param.annotation.model_json_schema(
229+
mode="serialization"
230+
)
229231
required_fields = model_schema.get("required", [])
230232
properties = model_schema.get("properties", {})
231233
for prop_name, prop_schema in properties.items():
@@ -337,7 +339,7 @@ def _serialize_response(result: Any) -> Any:
337339
from pydantic import BaseModel
338340

339341
if isinstance(result, BaseModel):
340-
return result.model_dump()
342+
return result.model_dump(by_alias=True)
341343
if isinstance(result, list):
342344
return [BaseRouter._serialize_response(item) for item in result]
343345
if isinstance(result, dict):
@@ -356,7 +358,7 @@ def _get_model_schema(cls, model: type[BaseModel], definitions: dict) -> dict:
356358
if cache_key not in cls._model_schema_cache:
357359
# Generate the schema if it's not in the cache
358360
model_schema = model.model_json_schema(
359-
ref_template="#/components/schemas/{model}"
361+
mode="serialization", ref_template="#/components/schemas/{model}"
360362
)
361363

362364
# Process and store nested definitions

tests/test_base_router.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest.mock import MagicMock
22

33
import pytest
4-
from pydantic import BaseModel
4+
from pydantic import BaseModel, computed_field
55

66
# Import the class under test
77
from fastopenapi.base_router import REDOC_URL, SWAGGER_URL, BaseRouter
@@ -13,6 +13,10 @@ class TestModel(BaseModel):
1313
age: int
1414
is_active: bool = True
1515

16+
@computed_field(alias="$active")
17+
def aliased_is_active(self) -> bool:
18+
return self.is_active
19+
1620

1721
class ResponseModel(BaseModel):
1822
id: int
@@ -30,7 +34,6 @@ class NestedListModel(BaseModel):
3034

3135

3236
class TestBaseRouter:
33-
3437
def setup_method(self):
3538
self.app_mock = MagicMock()
3639

@@ -465,6 +468,7 @@ def test_get_model_schema(self):
465468
assert "TestModel" in definitions
466469
assert definitions["TestModel"]["properties"]["name"]["type"] == "string"
467470
assert definitions["TestModel"]["properties"]["age"]["type"] == "integer"
471+
assert definitions["TestModel"]["properties"]["$active"]["type"] == "boolean"
468472

469473
def test_get_model_schema_with_nested_models(self):
470474
# Test getting schema for models with nested models
@@ -517,6 +521,7 @@ def endpoint(user: TestModel):
517521
assert result["user"].name == "John"
518522
assert result["user"].age == 30
519523
assert result["user"].is_active is True # Default value
524+
assert result["user"].aliased_is_active is True
520525

521526
def test_resolve_endpoint_params_missing_required(self):
522527
# Test error when required parameter is missing
@@ -566,15 +571,17 @@ def endpoint(query: TestModel):
566571
)
567572

568573
# Should convert model to query parameters
569-
assert len(params) == 3
574+
assert len(params) == 4
570575
assert params[0]["in"] == "query"
571576
assert params[1]["in"] == "query"
572577
assert params[2]["in"] == "query"
578+
assert params[3]["in"] == "query"
573579

574580
names = [p["name"] for p in params]
575581
assert "name" in names
576582
assert "age" in names
577583
assert "is_active" in names
584+
assert "$active" in names # alias
578585

579586
# No request body for GET
580587
assert body is None

0 commit comments

Comments
 (0)