Add validate_default param to Field, add tests

This commit is contained in:
Yurii Motov
2026-01-28 16:22:58 +01:00
parent 51aefc7987
commit 25232d15da
2 changed files with 70 additions and 2 deletions

View File

@@ -231,6 +231,7 @@ def Field(
allow_mutation: bool = True,
regex: Optional[str] = None,
discriminator: Optional[str] = None,
validate_default: Optional[bool] = None,
repr: bool = True,
primary_key: Union[bool, UndefinedType] = Undefined,
foreign_key: Any = Undefined,
@@ -275,6 +276,7 @@ def Field(
allow_mutation: bool = True,
regex: Optional[str] = None,
discriminator: Optional[str] = None,
validate_default: Optional[bool] = None,
repr: bool = True,
primary_key: Union[bool, UndefinedType] = Undefined,
foreign_key: str,
@@ -328,6 +330,7 @@ def Field(
allow_mutation: bool = True,
regex: Optional[str] = None,
discriminator: Optional[str] = None,
validate_default: Optional[bool] = None,
repr: bool = True,
sa_column: Union[Column[Any], UndefinedType] = Undefined,
schema_extra: Optional[dict[str, Any]] = None,
@@ -362,6 +365,7 @@ def Field(
allow_mutation: bool = True,
regex: Optional[str] = None,
discriminator: Optional[str] = None,
validate_default: Optional[bool] = None,
repr: bool = True,
primary_key: Union[bool, UndefinedType] = Undefined,
foreign_key: Any = Undefined,
@@ -377,7 +381,10 @@ def Field(
) -> Any:
current_schema_extra = schema_extra or {}
for param_name in ("coerce_numbers_to_str",):
for param_name in (
"coerce_numbers_to_str",
"validate_default",
):
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, UserWarning, stacklevel=2)
@@ -388,6 +395,9 @@ def Field(
current_coerce_numbers_to_str = coerce_numbers_to_str or current_schema_extra.pop(
"coerce_numbers_to_str", None
)
current_validate_default = validate_default or current_schema_extra.pop(
"validate_default", None
)
field_info_kwargs = {
"alias": alias,
"title": title,
@@ -396,6 +406,7 @@ def Field(
"include": include,
"const": const,
"coerce_numbers_to_str": current_coerce_numbers_to_str,
"validate_default": current_validate_default,
"gt": gt,
"ge": ge,
"lt": lt,

View File

@@ -3,7 +3,7 @@ from typing import Literal, Optional, Union
import pytest
from pydantic import ValidationError
from sqlmodel import Field, SQLModel
from sqlmodel import Field, Session, SQLModel, create_engine
def test_decimal():
@@ -87,3 +87,60 @@ def test_coerce_numbers_to_str_via_schema_extra(): # Current workaround. Remove
assert Model.model_validate({"val": 123}).val == "123"
assert Model.model_validate({"val": 45.67}).val == "45.67"
def test_validate_default_true():
class Model(SQLModel):
val: int = Field(default="123", validate_default=True)
assert Model.model_validate({}).val == 123
class Model2(SQLModel):
val: int = Field(default=None, validate_default=True)
with pytest.raises(ValidationError):
Model2.model_validate({})
def test_validate_default_table_model():
class Model(SQLModel):
id: Optional[int] = Field(default=None, primary_key=True)
val: int = Field(default="123", validate_default=True)
class ModelDB(Model, table=True):
pass
engine = create_engine("sqlite://", echo=True)
SQLModel.metadata.create_all(engine)
model = ModelDB()
with Session(engine) as session:
session.add(model)
session.commit()
session.refresh(model)
assert model.val == 123
@pytest.mark.parametrize("validate_default", [None, False])
def test_validate_default_false(validate_default: Optional[bool]):
class Model3(SQLModel):
val: int = Field(default="123", validate_default=validate_default)
assert Model3().val == "123"
def test_validate_default_via_schema_extra(): # Current workaround. Remove after some time
with pytest.warns(
UserWarning,
match=(
"Pass `validate_default` parameter directly to Field instead of passing "
"it via `schema_extra`"
),
):
class Model(SQLModel):
val: int = Field(default="123", schema_extra={"validate_default": True})
assert Model.model_validate({}).val == 123