Skip to content

Commit

Permalink
Enhance pydantic model schema generation
Browse files Browse the repository at this point in the history
- For pydantic based models, enhance the open api schema generation by differntiating between "validation" and "serialization" models.
- The default mode used by the pydantic TypeAdapter when generating JSON schemas is a "validation" schema.
- Add an additional option to the `model_schema` called `schema_mode` that will execute the TypeAdapter schema generation in the selected mode.
- When generating Open API paths for response models, change the schema_mode to "serialization" instead of "validation". This allows us to use models that have custom fields (e.g. computed fields or custom serialization logic) to be correctly converted to an Open API schema.
  • Loading branch information
bselman1 authored and pgjones committed May 15, 2024
1 parent c9eeadd commit 92d40f9
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 4 deletions.
8 changes: 5 additions & 3 deletions src/quart_schema/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dataclasses import fields, is_dataclass
from inspect import isclass
from typing import Any, Dict, List, Optional, Type, TypeVar, Union
from typing import Any, Dict, List, Literal, Optional, Type, TypeVar, Union

import humps
from quart import current_app
Expand Down Expand Up @@ -73,6 +73,8 @@ class MsgSpecValidationError(Exception): # type: ignore

T = TypeVar("T", bound=Model)

JsonSchemaMode = Literal['validation', 'serialization']


def convert_response_return_value(
result: ResponseReturnValue | HTTPException,
Expand Down Expand Up @@ -184,9 +186,9 @@ def model_load(
raise exception_class(error)


def model_schema(model_class: Type[Model], *, preference: Optional[str] = None) -> dict:
def model_schema(model_class: Type[Model], *, preference: Optional[str] = None, schema_mode: JsonSchemaMode = "validation") -> dict:
if _use_pydantic(model_class, preference):
return TypeAdapter(model_class).json_schema(ref_template=PYDANTIC_REF_TEMPLATE)
return TypeAdapter(model_class).json_schema(ref_template=PYDANTIC_REF_TEMPLATE, mode=schema_mode)
elif _use_msgspec(model_class, preference):
_, schema = schema_components([model_class], ref_template=MSGSPEC_REF_TEMPLATE)
return list(schema.values())[0]
Expand Down
4 changes: 3 additions & 1 deletion src/quart_schema/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,9 @@ def _build_path(func: Callable, rule: Rule, app: Quart) -> Tuple[dict, dict]:
for status_code in response_models.keys():
model_class, headers_model_class = response_models[status_code]
schema = model_schema(
model_class, preference=app.config["QUART_SCHEMA_CONVERSION_PREFERENCE"]
model_class,
preference=app.config["QUART_SCHEMA_CONVERSION_PREFERENCE"],
schema_mode="serialization",
)
definitions, schema = _split_convert_definitions(
schema, app.config["QUART_SCHEMA_CONVERT_CASING"]
Expand Down

0 comments on commit 92d40f9

Please sign in to comment.