]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:lock: Fix clone field implementation to handle sub-models in response_model (#889)
authorSebastián Ramírez <tiangolo@gmail.com>
Sat, 18 Jan 2020 17:03:51 +0000 (18:03 +0100)
committerGitHub <noreply@github.com>
Sat, 18 Jan 2020 17:03:51 +0000 (18:03 +0100)
fastapi/utils.py
tests/test_filter_pydantic_sub_model.py [new file with mode: 0644]

index a068cc582307b6566d143f7be4a5f0e2cc359e26..6a0c1bfd7e19b2ec30bb5574b22f226cc9553888 100644 (file)
@@ -97,7 +97,7 @@ def create_cloned_field(field: ModelField) -> ModelField:
             original_type.__name__, __config__=original_type.__config__
         )
         for f in original_type.__fields__.values():
-            use_type.__fields__[f.name] = f
+            use_type.__fields__[f.name] = create_cloned_field(f)
         use_type.__validators__ = original_type.__validators__
     if PYDANTIC_1:
         new_field = ModelField(
diff --git a/tests/test_filter_pydantic_sub_model.py b/tests/test_filter_pydantic_sub_model.py
new file mode 100644 (file)
index 0000000..1b22853
--- /dev/null
@@ -0,0 +1,91 @@
+from fastapi import Depends, FastAPI
+from pydantic import BaseModel
+from starlette.testclient import TestClient
+
+app = FastAPI()
+
+
+class ModelB(BaseModel):
+    username: str
+
+
+class ModelC(ModelB):
+    password: str
+
+
+class ModelA(BaseModel):
+    name: str
+    description: str = None
+    model_b: ModelB
+
+
+async def get_model_c() -> ModelC:
+    return ModelC(username="test-user", password="test-password")
+
+
+@app.get("/model", response_model=ModelA)
+async def get_model_a(model_c=Depends(get_model_c)):
+    return {"name": "model-a-name", "description": "model-a-desc", "model_b": model_c}
+
+
+client = TestClient(app)
+
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/model": {
+            "get": {
+                "summary": "Get Model A",
+                "operationId": "get_model_a_model_get",
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {
+                            "application/json": {
+                                "schema": {"$ref": "#/components/schemas/ModelA"}
+                            }
+                        },
+                    }
+                },
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "ModelA": {
+                "title": "ModelA",
+                "required": ["name", "model_b"],
+                "type": "object",
+                "properties": {
+                    "name": {"title": "Name", "type": "string"},
+                    "description": {"title": "Description", "type": "string"},
+                    "model_b": {"$ref": "#/components/schemas/ModelB"},
+                },
+            },
+            "ModelB": {
+                "title": "ModelB",
+                "required": ["username"],
+                "type": "object",
+                "properties": {"username": {"title": "Username", "type": "string"}},
+            },
+        }
+    },
+}
+
+
+def test_openapi_schema():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+
+
+def test_filter_sub_model():
+    response = client.get("/model")
+    assert response.status_code == 200
+    assert response.json() == {
+        "name": "model-a-name",
+        "description": "model-a-desc",
+        "model_b": {"username": "test-user"},
+    }