return model.model_config
+def _has_computed_fields(field: ModelField) -> bool:
+ computed_fields = field._type_adapter.core_schema.get("schema", {}).get(
+ "computed_fields", []
+ )
+ return len(computed_fields) > 0
+
+
def get_schema_from_model_field(
*,
field: ModelField,
],
separate_input_output_schemas: bool = True,
) -> Dict[str, Any]:
- computed_fields = field._type_adapter.core_schema.get("schema", {}).get(
- "computed_fields", []
- )
override_mode: Union[Literal["validation"], None] = (
None
- if (separate_input_output_schemas or len(computed_fields) > 0)
+ if (separate_input_output_schemas or _has_computed_fields(field))
else "validation"
)
# This expects that GenerateJsonSchema was already used to generate the definitions
Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
Dict[str, Dict[str, Any]],
]:
- has_computed_fields: bool = any(
- field._type_adapter.core_schema.get("schema", {}).get("computed_fields", [])
- for field in fields
- )
-
schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
- override_mode: Union[Literal["validation"], None] = (
- None if (separate_input_output_schemas or has_computed_fields) else "validation"
- )
validation_fields = [field for field in fields if field.mode == "validation"]
serialization_fields = [field for field in fields if field.mode == "serialization"]
flat_validation_models = get_flat_models_from_fields(
unique_flat_model_fields = {
f for f in flat_model_fields if f.type_ not in input_types
}
-
inputs = [
- (field, override_mode or field.mode, field._type_adapter.core_schema)
+ (
+ field,
+ (
+ field.mode
+ if (separate_input_output_schemas or _has_computed_fields(field))
+ else "validation"
+ ),
+ field._type_adapter.core_schema,
+ )
for field in list(fields) + list(unique_flat_model_fields)
]
field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs)
model_config = {"json_schema_serialization_defaults_required": True}
+if PYDANTIC_V2:
+ from pydantic import computed_field
+
+ class WithComputedField(BaseModel):
+ name: str
+
+ @computed_field
+ @property
+ def computed_field(self) -> str:
+ return f"computed {self.name}"
+
+
def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:
app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)
Item(name="Plumbus"),
]
+ if PYDANTIC_V2:
+
+ @app.post("/with-computed-field/")
+ def create_with_computed_field(
+ with_computed_field: WithComputedField,
+ ) -> WithComputedField:
+ return with_computed_field
+
client = TestClient(app)
return client
)
+@needs_pydanticv2
+def test_with_computed_field():
+ client = get_app_client()
+ client_no = get_app_client(separate_input_output_schemas=False)
+ response = client.post("/with-computed-field/", json={"name": "example"})
+ response2 = client_no.post("/with-computed-field/", json={"name": "example"})
+ assert response.status_code == response2.status_code == 200, response.text
+ assert (
+ response.json()
+ == response2.json()
+ == {
+ "name": "example",
+ "computed_field": "computed example",
+ }
+ )
+
+
@needs_pydanticv2
def test_openapi_schema():
client = get_app_client()
},
}
},
+ "/with-computed-field/": {
+ "post": {
+ "summary": "Create With Computed Field",
+ "operationId": "create_with_computed_field_with_computed_field__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WithComputedField-Input"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WithComputedField-Output"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
},
"components": {
"schemas": {
"required": ["subname", "sub_description", "tags"],
"title": "SubItem",
},
+ "WithComputedField-Input": {
+ "properties": {"name": {"type": "string", "title": "Name"}},
+ "type": "object",
+ "required": ["name"],
+ "title": "WithComputedField",
+ },
+ "WithComputedField-Output": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "computed_field": {
+ "type": "string",
+ "title": "Computed Field",
+ "readOnly": True,
+ },
+ },
+ "type": "object",
+ "required": ["name", "computed_field"],
+ "title": "WithComputedField",
+ },
"ValidationError": {
"properties": {
"loc": {
},
}
},
+ "/with-computed-field/": {
+ "post": {
+ "summary": "Create With Computed Field",
+ "operationId": "create_with_computed_field_with_computed_field__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WithComputedField-Input"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WithComputedField-Output"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
},
"components": {
"schemas": {
"required": ["subname"],
"title": "SubItem",
},
+ "WithComputedField-Input": {
+ "properties": {"name": {"type": "string", "title": "Name"}},
+ "type": "object",
+ "required": ["name"],
+ "title": "WithComputedField",
+ },
+ "WithComputedField-Output": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "computed_field": {
+ "type": "string",
+ "title": "Computed Field",
+ "readOnly": True,
+ },
+ },
+ "type": "object",
+ "required": ["name", "computed_field"],
+ "title": "WithComputedField",
+ },
"ValidationError": {
"properties": {
"loc": {