pythonasyncioapiasyncfastapiframeworkjsonjson-schemaopenapiopenapi3pydanticpython-typespython3redocreststarletteswaggerswagger-uiuvicornweb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
3.8 KiB
120 lines
3.8 KiB
"""
|
|
Test that Form-based dependency models preserve model_fields_set correctly.
|
|
|
|
This test addresses issue https://github.com/fastapi/fastapi/issues/13399
|
|
where Form models were pre-filling default values, making it impossible
|
|
to distinguish between explicitly set fields and fields using defaults.
|
|
"""
|
|
|
|
from typing import Annotated
|
|
|
|
import pytest
|
|
from fastapi import FastAPI, Form
|
|
from fastapi.testclient import TestClient
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class ModelWithDefaults(BaseModel):
|
|
field_bool: bool = True
|
|
field_str: str = "default_value"
|
|
field_int: int = 42
|
|
|
|
|
|
app = FastAPI()
|
|
|
|
|
|
@app.post("/json")
|
|
async def json_endpoint(model: ModelWithDefaults):
|
|
return {
|
|
"fields_set": list(model.model_fields_set),
|
|
"field_bool": model.field_bool,
|
|
"field_str": model.field_str,
|
|
"field_int": model.field_int,
|
|
}
|
|
|
|
|
|
@app.post("/form")
|
|
async def form_endpoint(model: Annotated[ModelWithDefaults, Form()]):
|
|
return {
|
|
"fields_set": list(model.model_fields_set),
|
|
"field_bool": model.field_bool,
|
|
"field_str": model.field_str,
|
|
"field_int": model.field_int,
|
|
}
|
|
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
def test_json_empty_preserves_fields_set():
|
|
"""JSON with empty dict should have empty fields_set"""
|
|
response = client.post("/json", json={})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["fields_set"] == []
|
|
assert data["field_bool"] is True
|
|
assert data["field_str"] == "default_value"
|
|
assert data["field_int"] == 42
|
|
|
|
|
|
def test_json_partial_preserves_fields_set():
|
|
"""JSON with partial data should only set provided fields"""
|
|
response = client.post("/json", json={"field_str": "custom"})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["fields_set"] == ["field_str"]
|
|
assert data["field_bool"] is True
|
|
assert data["field_str"] == "custom"
|
|
assert data["field_int"] == 42
|
|
|
|
|
|
def test_json_all_fields_preserves_fields_set():
|
|
"""JSON with all fields should mark all as set"""
|
|
response = client.post(
|
|
"/json",
|
|
json={"field_bool": False, "field_str": "custom", "field_int": 100},
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert set(data["fields_set"]) == {"field_bool", "field_str", "field_int"}
|
|
assert data["field_bool"] is False
|
|
assert data["field_str"] == "custom"
|
|
assert data["field_int"] == 100
|
|
|
|
|
|
def test_form_empty_preserves_fields_set():
|
|
"""Form with empty data should have empty fields_set (matching JSON behavior)"""
|
|
response = client.post("/form", data={})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# This is the key fix: Form should behave like JSON
|
|
assert data["fields_set"] == []
|
|
assert data["field_bool"] is True
|
|
assert data["field_str"] == "default_value"
|
|
assert data["field_int"] == 42
|
|
|
|
|
|
def test_form_partial_preserves_fields_set():
|
|
"""Form with partial data should only set provided fields (matching JSON behavior)"""
|
|
response = client.post("/form", data={"field_str": "custom"})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# This is the key fix: only field_str should be in fields_set
|
|
assert data["fields_set"] == ["field_str"]
|
|
assert data["field_bool"] is True
|
|
assert data["field_str"] == "custom"
|
|
assert data["field_int"] == 42
|
|
|
|
|
|
def test_form_all_fields_preserves_fields_set():
|
|
"""Form with all fields should mark all as set"""
|
|
response = client.post(
|
|
"/form",
|
|
data={"field_bool": "false", "field_str": "custom", "field_int": "100"},
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert set(data["fields_set"]) == {"field_bool", "field_str", "field_int"}
|
|
assert data["field_bool"] is False
|
|
assert data["field_str"] == "custom"
|
|
assert data["field_int"] == 100
|
|
|