From: Yurii Motov Date: Wed, 28 Jan 2026 13:31:02 +0000 (+0100) Subject: Add `exclude_if` param to Field, add tests X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3300d7bfbba88c6e3e98cc650fb409f5d2ad581;p=thirdparty%2Ffastapi%2Fsqlmodel.git Add `exclude_if` param to Field, add tests --- diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 7fbfbe57..cae0f986 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -216,6 +216,7 @@ def Field( examples: Optional[list[Any]] = None, deprecated: Union[Deprecated, str, bool, None] = None, exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, + exclude_if: Optional[Callable[[Any], bool]] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, gt: Optional[float] = None, @@ -262,6 +263,7 @@ def Field( examples: Optional[list[Any]] = None, deprecated: Union[Deprecated, str, bool, None] = None, exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, + exclude_if: Optional[Callable[[Any], bool]] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, gt: Optional[float] = None, @@ -317,6 +319,7 @@ def Field( examples: Optional[list[Any]] = None, deprecated: Union[Deprecated, str, bool, None] = None, exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, + exclude_if: Optional[Callable[[Any], bool]] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, gt: Optional[float] = None, @@ -353,6 +356,7 @@ def Field( examples: Optional[list[Any]] = None, deprecated: Union[Deprecated, str, bool, None] = None, exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, + exclude_if: Optional[Callable[[Any], bool]] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, gt: Optional[float] = None, @@ -386,7 +390,7 @@ def Field( ) -> Any: current_schema_extra = schema_extra or {} - for param_name in ("strict", "examples", "deprecated"): + for param_name in ("strict", "examples", "deprecated", "exclude_if"): if param_name in current_schema_extra: msg = f"Pass `{param_name}` parameter directly to Field instead of passing it via `schema_extra`" warnings.warn(msg, DeprecationWarning, stacklevel=2) @@ -397,6 +401,7 @@ def Field( current_strict = strict or current_schema_extra.pop("strict", None) current_examples = examples or current_schema_extra.pop("examples", None) current_deprecated = deprecated or current_schema_extra.pop("deprecated", None) + current_exclude_if = exclude_if or current_schema_extra.pop("exclude_if", None) field_info_kwargs = { "alias": alias, "title": title, @@ -404,6 +409,7 @@ def Field( "examples": current_examples, "deprecated": current_deprecated, "exclude": exclude, + "exclude_if": current_exclude_if, "include": include, "const": const, "gt": gt, diff --git a/tests/test_pydantic/test_field.py b/tests/test_pydantic/test_field.py index 4d6aa86c..d97640bf 100644 --- a/tests/test_pydantic/test_field.py +++ b/tests/test_pydantic/test_field.py @@ -1,5 +1,5 @@ from decimal import Decimal -from typing import Literal, Optional, Union +from typing import Any, Literal, Optional, Union import pytest from pydantic import ValidationError @@ -175,3 +175,48 @@ def test_deprecated_via_schema_extra(): # Current workaround. Remove after some model_schema = Model.model_json_schema() assert model_schema["properties"]["old_field"]["deprecated"] is True assert model_schema["properties"]["another_old_field"]["deprecated"] is True + + +def test_exclude_if(): + def is_empty_string(value: Any) -> bool: + return value == "" + + class Model(SQLModel): + name: str = Field(exclude_if=is_empty_string) + age: int + + model1 = Model(name="Alice", age=30) + model2 = Model(name="", age=25) + + dict1 = model1.model_dump() + dict2 = model2.model_dump() + + assert "name" in dict1 + assert dict1["name"] == "Alice" + + assert "name" not in dict2 + + +def test_exclude_if_via_schema_extra(): + def is_empty_string(value: Any) -> bool: + return value == "" + + with pytest.warns( + DeprecationWarning, + match="Pass `exclude_if` parameter directly to Field instead of passing it via `schema_extra`", + ): + + class Model(SQLModel): + name: str = Field(schema_extra={"exclude_if": is_empty_string}) + age: int + + model1 = Model(name="Alice", age=30) + model2 = Model(name="", age=25) + + dict1 = model1.model_dump() + dict2 = model2.model_dump() + + assert "name" in dict1 + assert dict1["name"] == "Alice" + + assert "name" not in dict2