mirror of https://github.com/tiangolo/fastapi.git
🐛 Fix automatic embedding with dependencies and sub-dependencies (#1079)
* Handle automatic embedding with Depends * 🐛 Fix body embeds for sub-dependencies and simplify implementation * ✅ Add/update tests for body embeds in dependencies * 👷 Trigger Travis Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
This commit is contained in:
parent
042c697b6b
commit
90afc72e64
|
|
@ -704,8 +704,14 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
|||
first_param = flat_dependant.body_params[0]
|
||||
field_info = get_field_info(first_param)
|
||||
embed = getattr(field_info, "embed", None)
|
||||
if len(flat_dependant.body_params) == 1 and not embed:
|
||||
body_param_names_set = set([param.name for param in flat_dependant.body_params])
|
||||
if len(body_param_names_set) == 1 and not embed:
|
||||
return get_schema_compatible_field(field=first_param)
|
||||
# If one field requires to embed, all have to be embedded
|
||||
# in case a sub-dependency is evaluated with a single unique body field
|
||||
# That is combined (embedded) with other body fields
|
||||
for param in flat_dependant.body_params:
|
||||
setattr(get_field_info(param), "embed", True)
|
||||
model_name = "Body_" + name
|
||||
BodyModel = create_model(model_name)
|
||||
for f in flat_dependant.body_params:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
data: str
|
||||
|
||||
|
||||
def duplicate_dependency(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
def dependency(item2: Item):
|
||||
return item2
|
||||
|
||||
|
||||
def sub_duplicate_dependency(
|
||||
item: Item, sub_item: Item = Depends(duplicate_dependency)
|
||||
):
|
||||
return [item, sub_item]
|
||||
|
||||
|
||||
@app.post("/with-duplicates")
|
||||
async def with_duplicates(item: Item, item2: Item = Depends(duplicate_dependency)):
|
||||
return [item, item2]
|
||||
|
||||
|
||||
@app.post("/no-duplicates")
|
||||
async def no_duplicates(item: Item, item2: Item = Depends(dependency)):
|
||||
return [item, item2]
|
||||
|
||||
|
||||
@app.post("/with-duplicates-sub")
|
||||
async def no_duplicates_sub(
|
||||
item: Item, sub_items: List[Item] = Depends(sub_duplicate_dependency)
|
||||
):
|
||||
return [item, sub_items]
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/with-duplicates": {
|
||||
"post": {
|
||||
"summary": "With Duplicates",
|
||||
"operationId": "with_duplicates_with_duplicates_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"/no-duplicates": {
|
||||
"post": {
|
||||
"summary": "No Duplicates",
|
||||
"operationId": "no_duplicates_no_duplicates_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Body_no_duplicates_no_duplicates_post"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"/with-duplicates-sub": {
|
||||
"post": {
|
||||
"summary": "No Duplicates Sub",
|
||||
"operationId": "no_duplicates_sub_with_duplicates_sub_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Body_no_duplicates_no_duplicates_post": {
|
||||
"title": "Body_no_duplicates_no_duplicates_post",
|
||||
"required": ["item", "item2"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"item": {"$ref": "#/components/schemas/Item"},
|
||||
"item2": {"$ref": "#/components/schemas/Item"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
"Item": {
|
||||
"title": "Item",
|
||||
"required": ["data"],
|
||||
"type": "object",
|
||||
"properties": {"data": {"title": "Data", "type": "string"}},
|
||||
},
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_no_duplicates_invalid():
|
||||
response = client.post("/no-duplicates", json={"item": {"data": "myitem"}})
|
||||
assert response.status_code == 422
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "item2"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_no_duplicates():
|
||||
response = client.post(
|
||||
"/no-duplicates",
|
||||
json={"item": {"data": "myitem"}, "item2": {"data": "myitem2"}},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [{"data": "myitem"}, {"data": "myitem2"}]
|
||||
|
||||
|
||||
def test_duplicates():
|
||||
response = client.post("/with-duplicates", json={"data": "myitem"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [{"data": "myitem"}, {"data": "myitem"}]
|
||||
|
||||
|
||||
def test_sub_duplicates():
|
||||
response = client.post("/with-duplicates-sub", json={"data": "myitem"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [
|
||||
{"data": "myitem"},
|
||||
[{"data": "myitem"}, {"data": "myitem"}],
|
||||
]
|
||||
Loading…
Reference in New Issue