From b98a3b3398813cc005fcf51216868126e17ba0db Mon Sep 17 00:00:00 2001 From: Yurii Motov Date: Thu, 29 Jan 2026 11:20:22 +0100 Subject: [PATCH 1/2] Allow values that support `SupportsGt` (and others) protocol for `gt` (and others) in `Field` --- sqlmodel/main.py | 33 ++++++------- tests/test_pydantic/test_field.py | 80 +++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 16 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 84478f24cf..e8c264ac68 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -22,6 +22,7 @@ overload, ) +import annotated_types from pydantic import BaseModel, EmailStr from pydantic.fields import FieldInfo as PydanticFieldInfo from sqlalchemy import ( @@ -214,10 +215,10 @@ def Field( exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, + gt: Optional[annotated_types.SupportsGt] = None, + ge: Optional[annotated_types.SupportsGe] = None, + lt: Optional[annotated_types.SupportsLt] = None, + le: Optional[annotated_types.SupportsLe] = None, multiple_of: Optional[float] = None, max_digits: Optional[int] = None, decimal_places: Optional[int] = None, @@ -257,10 +258,10 @@ def Field( exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, + gt: Optional[annotated_types.SupportsGt] = None, + ge: Optional[annotated_types.SupportsGe] = None, + lt: Optional[annotated_types.SupportsLt] = None, + le: Optional[annotated_types.SupportsLe] = None, multiple_of: Optional[float] = None, max_digits: Optional[int] = None, decimal_places: Optional[int] = None, @@ -309,10 +310,10 @@ def Field( exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, + gt: Optional[annotated_types.SupportsGt] = None, + ge: Optional[annotated_types.SupportsGe] = None, + lt: Optional[annotated_types.SupportsLt] = None, + le: Optional[annotated_types.SupportsLe] = None, multiple_of: Optional[float] = None, max_digits: Optional[int] = None, decimal_places: Optional[int] = None, @@ -342,10 +343,10 @@ def Field( exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None, const: Optional[bool] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, + gt: Optional[annotated_types.SupportsGt] = None, + ge: Optional[annotated_types.SupportsGe] = None, + lt: Optional[annotated_types.SupportsLt] = None, + le: Optional[annotated_types.SupportsLe] = None, multiple_of: Optional[float] = None, max_digits: Optional[int] = None, decimal_places: Optional[int] = None, diff --git a/tests/test_pydantic/test_field.py b/tests/test_pydantic/test_field.py index 140b02fd9b..c7c67f1fcf 100644 --- a/tests/test_pydantic/test_field.py +++ b/tests/test_pydantic/test_field.py @@ -54,3 +54,83 @@ class Model(SQLModel): instance = Model(id=123, foo="bar") assert "foo=" not in repr(instance) + + +def test_gt(): + class Model(SQLModel): + int_value: int = Field(gt=10) + tuple_value: tuple[int, int] = Field(gt=(1, 2)) + + Model(int_value=11, tuple_value=(1, 3)) + + with pytest.raises(ValidationError) as exc_info: + Model(int_value=10, tuple_value=(1, 3)) + assert len(exc_info.value.errors()) == 1 + assert exc_info.value.errors()[0]["type"] == "greater_than" + assert exc_info.value.errors()[0]["loc"] == ("int_value",) + + with pytest.raises(ValidationError) as exc_info_2: + Model(int_value=11, tuple_value=(1, 2)) + assert len(exc_info_2.value.errors()) == 1 + assert exc_info_2.value.errors()[0]["type"] == "greater_than" + assert exc_info_2.value.errors()[0]["loc"] == ("tuple_value",) + + +def test_ge(): + class Model(SQLModel): + int_value: int = Field(ge=10) + tuple_value: tuple[int, int] = Field(ge=(1, 2)) + + Model(int_value=10, tuple_value=(1, 2)) + + with pytest.raises(ValidationError) as exc_info: + Model(int_value=9, tuple_value=(1, 2)) + assert len(exc_info.value.errors()) == 1 + assert exc_info.value.errors()[0]["type"] == "greater_than_equal" + assert exc_info.value.errors()[0]["loc"] == ("int_value",) + + with pytest.raises(ValidationError) as exc_info_2: + Model(int_value=10, tuple_value=(1, 1)) + assert len(exc_info_2.value.errors()) == 1 + assert exc_info_2.value.errors()[0]["type"] == "greater_than_equal" + assert exc_info_2.value.errors()[0]["loc"] == ("tuple_value",) + + +def test_lt(): + class Model(SQLModel): + int_value: int = Field(lt=10) + tuple_value: tuple[int, int] = Field(lt=(1, 2)) + + Model(int_value=9, tuple_value=(1, 1)) + + with pytest.raises(ValidationError) as exc_info: + Model(int_value=10, tuple_value=(1, 1)) + assert len(exc_info.value.errors()) == 1 + assert exc_info.value.errors()[0]["type"] == "less_than" + assert exc_info.value.errors()[0]["loc"] == ("int_value",) + + with pytest.raises(ValidationError) as exc_info_2: + Model(int_value=9, tuple_value=(1, 2)) + assert len(exc_info_2.value.errors()) == 1 + assert exc_info_2.value.errors()[0]["type"] == "less_than" + assert exc_info_2.value.errors()[0]["loc"] == ("tuple_value",) + + +def test_le(): + class Model(SQLModel): + int_value: int = Field(le=10) + tuple_value: tuple[int, int] = Field(le=(1, 2)) + + Model(int_value=10, tuple_value=(1, 2)) + + with pytest.raises(ValidationError) as exc_info: + Model(int_value=11, tuple_value=(1, 2)) + assert len(exc_info.value.errors()) == 1 + assert exc_info.value.errors()[0]["type"] == "less_than_equal" + assert exc_info.value.errors()[0]["loc"] == ("int_value",) + + with pytest.raises(ValidationError) as exc_info_2: + Model(int_value=10, tuple_value=(1, 3)) + assert len(exc_info_2.value.errors()) == 1 + assert exc_info_2.value.errors()[0]["type"] == "less_than_equal" + assert exc_info_2.value.errors()[0]["loc"] == ("tuple_value",) From 2e41f9d8a7f20db96a1fe78d5803685f051bfccd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 20 Feb 2026 21:31:50 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8E=A8=20Auto=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index ed3db3df6e..4648ce9e01 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -251,7 +251,7 @@ def Field( gt: annotated_types.SupportsGt | None = None, ge: annotated_types.SupportsGe | None = None, lt: annotated_types.SupportsLt | None = None, - le: annotated_types.SupportsLe | None= None, + le: annotated_types.SupportsLe | None = None, multiple_of: float | None = None, max_digits: int | None = None, decimal_places: int | None = None, @@ -294,7 +294,7 @@ def Field( gt: annotated_types.SupportsGt | None = None, ge: annotated_types.SupportsGe | None = None, lt: annotated_types.SupportsLt | None = None, - le: annotated_types.SupportsLe | None= None, + le: annotated_types.SupportsLe | None = None, multiple_of: float | None = None, max_digits: int | None = None, decimal_places: int | None = None, @@ -346,7 +346,7 @@ def Field( gt: annotated_types.SupportsGt | None = None, ge: annotated_types.SupportsGe | None = None, lt: annotated_types.SupportsLt | None = None, - le: annotated_types.SupportsLe | None= None, + le: annotated_types.SupportsLe | None = None, multiple_of: float | None = None, max_digits: int | None = None, decimal_places: int | None = None, @@ -379,7 +379,7 @@ def Field( gt: annotated_types.SupportsGt | None = None, ge: annotated_types.SupportsGe | None = None, lt: annotated_types.SupportsLt | None = None, - le: annotated_types.SupportsLe | None= None, + le: annotated_types.SupportsLe | None = None, multiple_of: float | None = None, max_digits: int | None = None, decimal_places: int | None = None,