]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
🐛 Fix separation of schemas with nested models introduced in 0.119.0 (#14246)
authorSebastián Ramírez <tiangolo@gmail.com>
Wed, 29 Oct 2025 13:09:30 +0000 (10:09 -0300)
committerGitHub <noreply@github.com>
Wed, 29 Oct 2025 13:09:30 +0000 (14:09 +0100)
fastapi/_compat/v2.py
tests/test_no_schema_split.py [new file with mode: 0644]
tests/test_pydantic_v1_v2_multifile/test_multifile.py

index fb2c691d8724bdb7c8a484f56c11235106a4dd46..6a87b9ae9df12024eb8b57413e74f43323ca76ff 100644 (file)
@@ -207,11 +207,31 @@ def get_definitions(
     override_mode: Union[Literal["validation"], None] = (
         None if separate_input_output_schemas else "validation"
     )
-    flat_models = get_flat_models_from_fields(fields, known_models=set())
-    flat_model_fields = [
-        ModelField(field_info=FieldInfo(annotation=model), name=model.__name__)
-        for model in flat_models
+    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(
+        validation_fields, known_models=set()
+    )
+    flat_serialization_models = get_flat_models_from_fields(
+        serialization_fields, known_models=set()
+    )
+    flat_validation_model_fields = [
+        ModelField(
+            field_info=FieldInfo(annotation=model),
+            name=model.__name__,
+            mode="validation",
+        )
+        for model in flat_validation_models
+    ]
+    flat_serialization_model_fields = [
+        ModelField(
+            field_info=FieldInfo(annotation=model),
+            name=model.__name__,
+            mode="serialization",
+        )
+        for model in flat_serialization_models
     ]
+    flat_model_fields = flat_validation_model_fields + flat_serialization_model_fields
     input_types = {f.type_ for f in fields}
     unique_flat_model_fields = {
         f for f in flat_model_fields if f.type_ not in input_types
diff --git a/tests/test_no_schema_split.py b/tests/test_no_schema_split.py
new file mode 100644 (file)
index 0000000..b0b5958
--- /dev/null
@@ -0,0 +1,203 @@
+# Test with parts from, and to verify the report in:
+# https://github.com/fastapi/fastapi/discussions/14177
+# Made an issue in:
+# https://github.com/fastapi/fastapi/issues/14247
+from enum import Enum
+from typing import List
+
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+from pydantic import BaseModel, Field
+
+from tests.utils import pydantic_snapshot
+
+
+class MessageEventType(str, Enum):
+    alpha = "alpha"
+    beta = "beta"
+
+
+class MessageEvent(BaseModel):
+    event_type: MessageEventType = Field(default=MessageEventType.alpha)
+    output: str
+
+
+class MessageOutput(BaseModel):
+    body: str = ""
+    events: List[MessageEvent] = []
+
+
+class Message(BaseModel):
+    input: str
+    output: MessageOutput
+
+
+app = FastAPI(title="Minimal FastAPI App", version="1.0.0")
+
+
+@app.post("/messages", response_model=Message)
+async def create_message(input_message: str) -> Message:
+    return Message(
+        input=input_message,
+        output=MessageOutput(body=f"Processed: {input_message}"),
+    )
+
+
+client = TestClient(app)
+
+
+def test_create_message():
+    response = client.post("/messages", params={"input_message": "Hello"})
+    assert response.status_code == 200, response.text
+    assert response.json() == {
+        "input": "Hello",
+        "output": {"body": "Processed: Hello", "events": []},
+    }
+
+
+def test_openapi_schema():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200, response.text
+    assert response.json() == snapshot(
+        {
+            "openapi": "3.1.0",
+            "info": {"title": "Minimal FastAPI App", "version": "1.0.0"},
+            "paths": {
+                "/messages": {
+                    "post": {
+                        "summary": "Create Message",
+                        "operationId": "create_message_messages_post",
+                        "parameters": [
+                            {
+                                "name": "input_message",
+                                "in": "query",
+                                "required": True,
+                                "schema": {"type": "string", "title": "Input Message"},
+                            }
+                        ],
+                        "responses": {
+                            "200": {
+                                "description": "Successful Response",
+                                "content": {
+                                    "application/json": {
+                                        "schema": {
+                                            "$ref": "#/components/schemas/Message"
+                                        }
+                                    }
+                                },
+                            },
+                            "422": {
+                                "description": "Validation Error",
+                                "content": {
+                                    "application/json": {
+                                        "schema": {
+                                            "$ref": "#/components/schemas/HTTPValidationError"
+                                        }
+                                    }
+                                },
+                            },
+                        },
+                    }
+                }
+            },
+            "components": {
+                "schemas": {
+                    "HTTPValidationError": {
+                        "properties": {
+                            "detail": {
+                                "items": {
+                                    "$ref": "#/components/schemas/ValidationError"
+                                },
+                                "type": "array",
+                                "title": "Detail",
+                            }
+                        },
+                        "type": "object",
+                        "title": "HTTPValidationError",
+                    },
+                    "Message": {
+                        "properties": {
+                            "input": {"type": "string", "title": "Input"},
+                            "output": {"$ref": "#/components/schemas/MessageOutput"},
+                        },
+                        "type": "object",
+                        "required": ["input", "output"],
+                        "title": "Message",
+                    },
+                    "MessageEvent": {
+                        "properties": {
+                            "event_type": pydantic_snapshot(
+                                v2=snapshot(
+                                    {
+                                        "$ref": "#/components/schemas/MessageEventType",
+                                        "default": "alpha",
+                                    }
+                                ),
+                                v1=snapshot(
+                                    {
+                                        "allOf": [
+                                            {
+                                                "$ref": "#/components/schemas/MessageEventType"
+                                            }
+                                        ],
+                                        "default": "alpha",
+                                    }
+                                ),
+                            ),
+                            "output": {"type": "string", "title": "Output"},
+                        },
+                        "type": "object",
+                        "required": ["output"],
+                        "title": "MessageEvent",
+                    },
+                    "MessageEventType": pydantic_snapshot(
+                        v2=snapshot(
+                            {
+                                "type": "string",
+                                "enum": ["alpha", "beta"],
+                                "title": "MessageEventType",
+                            }
+                        ),
+                        v1=snapshot(
+                            {
+                                "type": "string",
+                                "enum": ["alpha", "beta"],
+                                "title": "MessageEventType",
+                                "description": "An enumeration.",
+                            }
+                        ),
+                    ),
+                    "MessageOutput": {
+                        "properties": {
+                            "body": {"type": "string", "title": "Body", "default": ""},
+                            "events": {
+                                "items": {"$ref": "#/components/schemas/MessageEvent"},
+                                "type": "array",
+                                "title": "Events",
+                                "default": [],
+                            },
+                        },
+                        "type": "object",
+                        "title": "MessageOutput",
+                    },
+                    "ValidationError": {
+                        "properties": {
+                            "loc": {
+                                "items": {
+                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
+                                },
+                                "type": "array",
+                                "title": "Location",
+                            },
+                            "msg": {"type": "string", "title": "Message"},
+                            "type": {"type": "string", "title": "Error Type"},
+                        },
+                        "type": "object",
+                        "required": ["loc", "msg", "type"],
+                        "title": "ValidationError",
+                    },
+                }
+            },
+        }
+    )
index 4472bd73e3bab4cb99a6cc6827a6b62b80011e11..e66d102fb35d02ba1bd039a609a2d2d9f1e5cd7f 100644 (file)
@@ -1028,17 +1028,6 @@ def test_openapi_schema():
                                 "type": "object",
                                 "title": "HTTPValidationError",
                             },
-                            "SubItem-Output": {
-                                "properties": {
-                                    "new_sub_name": {
-                                        "type": "string",
-                                        "title": "New Sub Name",
-                                    }
-                                },
-                                "type": "object",
-                                "required": ["new_sub_name"],
-                                "title": "SubItem",
-                            },
                             "ValidationError": {
                                 "properties": {
                                     "loc": {
@@ -1113,11 +1102,11 @@ def test_openapi_schema():
                                         "title": "New Description",
                                     },
                                     "new_sub": {
-                                        "$ref": "#/components/schemas/SubItem-Output"
+                                        "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
                                     },
                                     "new_multi": {
                                         "items": {
-                                            "$ref": "#/components/schemas/SubItem-Output"
+                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
                                         },
                                         "type": "array",
                                         "title": "New Multi",