]> git.ipfire.org Git - thirdparty/fastapi/sqlmodel.git/commitdiff
Add `exclude_if` param to Field, add tests
authorYurii Motov <yurii.motov.monte@gmail.com>
Wed, 28 Jan 2026 13:31:02 +0000 (14:31 +0100)
committerYurii Motov <yurii.motov.monte@gmail.com>
Wed, 28 Jan 2026 17:02:49 +0000 (18:02 +0100)
sqlmodel/main.py
tests/test_pydantic/test_field.py

index 7fbfbe57458a3bdc77fcd1799a49c8ffa4c76059..cae0f986c345f4d6422cb46884f2f7d922dd17b2 100644 (file)
@@ -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,
index 4d6aa86c12e19ed854940384a4a73a9161e873db..d97640bfd144b7145415745b41601888bc8a8f69 100644 (file)
@@ -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