Browse Source

drop pydantic v1/v2 switching in forms defaults tests

pull/13464/head
sneakers-the-rat 2 months ago
parent
commit
c7adb75b8e
No known key found for this signature in database GPG Key ID: 6DCB96EF1E4D232D
  1. 171
      tests/test_forms_defaults.py

171
tests/test_forms_defaults.py

@ -2,17 +2,9 @@ from typing import Annotated
import pytest
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, model_validator
from starlette.testclient import TestClient
from .utils import needs_pydanticv2
if PYDANTIC_V2:
from pydantic import model_validator
else:
from pydantic import root_validator
def _validate_input(value: dict) -> dict:
"""
@ -33,16 +25,9 @@ class Parent(BaseModel):
init_input: dict
# importantly, no default here
if PYDANTIC_V2:
@model_validator(mode="before")
def validate_inputs(cls, value: dict) -> dict:
return _validate_input(value)
else:
@root_validator(pre=True)
def validate_inputs(cls, value: dict) -> dict:
return _validate_input(value)
@model_validator(mode="before")
def validate_inputs(cls, value: dict) -> dict:
return _validate_input(value)
class StandardModel(Parent):
@ -63,29 +48,29 @@ class FieldModel(Parent):
true_if_unset: bool | None = Field(default=None)
if PYDANTIC_V2:
class AnnotatedFieldModel(Parent):
default_true: Annotated[bool, Field(default=True)]
default_false: Annotated[bool, Field(default=False)]
default_none: Annotated[bool | None, Field(default=None)]
default_zero: Annotated[int, Field(default=0)]
default_str: Annotated[str, Field(default="foo")]
true_if_unset: Annotated[bool | None, Field(default=None)]
class AnnotatedFieldModel(Parent):
default_true: Annotated[bool, Field(default=True)]
default_false: Annotated[bool, Field(default=False)]
default_none: Annotated[bool | None, Field(default=None)]
default_zero: Annotated[int, Field(default=0)]
default_str: Annotated[str, Field(default="foo")]
true_if_unset: Annotated[bool | None, Field(default=None)]
class AnnotatedFormModel(Parent):
default_true: Annotated[bool, Form(default=True)]
default_false: Annotated[bool, Form(default=False)]
default_none: Annotated[bool | None, Form(default=None)]
default_zero: Annotated[int, Form(default=0)]
default_str: Annotated[str, Form(default="foo")]
true_if_unset: Annotated[bool | None, Form(default=None)]
class AnnotatedFormModel(Parent):
default_true: Annotated[bool, Form(default=True)]
default_false: Annotated[bool, Form(default=False)]
default_none: Annotated[bool | None, Form(default=None)]
default_zero: Annotated[int, Form(default=0)]
default_str: Annotated[str, Form(default="foo")]
true_if_unset: Annotated[bool | None, Form(default=None)]
class SimpleForm(BaseModel):
"""https://github.com/fastapi/fastapi/pull/13464#issuecomment-2708378172"""
foo: Annotated[str, Form(default="bar")]
alias_with: Annotated[str, Form(alias="with", default="nothing")]
class SimpleForm(BaseModel):
"""https://github.com/fastapi/fastapi/pull/13464#issuecomment-2708378172"""
foo: Annotated[str, Form(default="bar")]
alias_with: Annotated[str, Form(alias="with", default="nothing")]
class ResponseModel(BaseModel):
@ -98,26 +83,16 @@ class ResponseModel(BaseModel):
@classmethod
def from_value(cls, value: Parent) -> "ResponseModel":
if PYDANTIC_V2:
return ResponseModel(
init_input=value.init_input,
fields_set=list(value.model_fields_set),
dumped_fields_no_exclude=value.model_dump(),
dumped_fields_exclude_default=value.model_dump(exclude_defaults=True),
dumped_fields_exclude_unset=value.model_dump(exclude_unset=True),
dumped_fields_no_meta=value.model_dump(
exclude={"init_input", "fields_set"}
),
)
else:
return ResponseModel(
init_input=value.init_input,
fields_set=list(value.__fields_set__),
dumped_fields_no_exclude=value.dict(),
dumped_fields_exclude_default=value.dict(exclude_defaults=True),
dumped_fields_exclude_unset=value.dict(exclude_unset=True),
dumped_fields_no_meta=value.dict(exclude={"init_input", "fields_set"}),
)
return ResponseModel(
init_input=value.init_input,
fields_set=list(value.model_fields_set),
dumped_fields_no_exclude=value.model_dump(),
dumped_fields_exclude_default=value.model_dump(exclude_defaults=True),
dumped_fields_exclude_unset=value.model_dump(exclude_unset=True),
dumped_fields_no_meta=value.model_dump(
exclude={"init_input", "fields_set"}
),
)
app = FastAPI()
@ -133,19 +108,18 @@ async def form_field(value: Annotated[FieldModel, Form()]) -> ResponseModel:
return ResponseModel.from_value(value)
if PYDANTIC_V2:
@app.post("/form/annotated-field")
async def form_annotated_field(
value: Annotated[AnnotatedFieldModel, Form()],
) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/form/annotated-field")
async def form_annotated_field(
value: Annotated[AnnotatedFieldModel, Form()],
) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/form/annotated-form")
async def form_annotated_form(
value: Annotated[AnnotatedFormModel, Form()],
) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/form/annotated-form")
async def form_annotated_form(
value: Annotated[AnnotatedFormModel, Form()],
) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/form/inlined")
@ -188,34 +162,28 @@ async def json_field(value: FieldModel) -> ResponseModel:
return ResponseModel.from_value(value)
if PYDANTIC_V2:
@app.post("/json/annotated-field")
async def json_annotated_field(value: AnnotatedFieldModel) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/json/annotated-field")
async def json_annotated_field(value: AnnotatedFieldModel) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/json/annotated-form")
async def json_annotated_form(value: AnnotatedFormModel) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/json/annotated-form")
async def json_annotated_form(value: AnnotatedFormModel) -> ResponseModel:
return ResponseModel.from_value(value)
@app.post("/simple-form")
def form_endpoint(model: Annotated[SimpleForm, Form()]) -> dict:
"""https://github.com/fastapi/fastapi/pull/13464#issuecomment-2708378172"""
return model.model_dump()
@app.post("/simple-form")
def form_endpoint(model: Annotated[SimpleForm, Form()]) -> dict:
"""https://github.com/fastapi/fastapi/pull/13464#issuecomment-2708378172"""
return model.model_dump()
if PYDANTIC_V2:
MODEL_TYPES = {
"standard": StandardModel,
"field": FieldModel,
"annotated-field": AnnotatedFieldModel,
"annotated-form": AnnotatedFormModel,
}
else:
MODEL_TYPES = {
"standard": StandardModel,
"field": FieldModel,
}
MODEL_TYPES = {
"standard": StandardModel,
"field": FieldModel,
"annotated-field": AnnotatedFieldModel,
"annotated-form": AnnotatedFormModel,
}
ENCODINGS = ("form", "json")
@ -260,18 +228,12 @@ def test_no_prefill_defaults_partially_set(encoding, model_type, client):
data = {"true_if_unset": False, "default_false": True, "default_zero": 0}
res = client.post(endpoint, json=data)
if PYDANTIC_V2:
dumped_exclude_unset = MODEL_TYPES[model_type](**data).model_dump(
exclude_unset=True
)
dumped_exclude_default = MODEL_TYPES[model_type](**data).model_dump(
exclude_defaults=True
)
else:
dumped_exclude_unset = MODEL_TYPES[model_type](**data).dict(exclude_unset=True)
dumped_exclude_default = MODEL_TYPES[model_type](**data).dict(
exclude_defaults=True
)
dumped_exclude_unset = MODEL_TYPES[model_type](**data).model_dump(
exclude_unset=True
)
dumped_exclude_default = MODEL_TYPES[model_type](**data).model_dump(
exclude_defaults=True
)
assert res.status_code == 200
response_model = ResponseModel(**res.json())
@ -283,7 +245,6 @@ def test_no_prefill_defaults_partially_set(encoding, model_type, client):
assert "default_zero" not in response_model.dumped_fields_exclude_default
@needs_pydanticv2
def test_casted_empty_defaults(client: TestClient):
"""https://github.com/fastapi/fastapi/pull/13464#issuecomment-2708378172"""
form_content = {"foo": "", "with": ""}

Loading…
Cancel
Save