From 66e1bfe3d1db2e502416897e32e3fd444d28bc47 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 05:26:06 +0200 Subject: [PATCH 01/14] Fix sqlmodel field being overide by pydantic --- sqlmodel/main.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 7c916f79af..536ae48fcb 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -562,7 +562,14 @@ def get_config(name: str) -> Any: # If it was passed by kwargs, ensure it's also set in config set_config_value(model=new_cls, parameter="table", value=config_table) for k, v in get_model_fields(new_cls).items(): - col = get_column_from_field(v) + original_field = getattr(v, "_original_assignment", Undefined) + annotated_field_meta = new_cls.__annotations__[k].__dict__.get("__metadata__", []) + annotated_field = next((f for f in annotated_field_meta if isinstance(f, FieldInfo)), None) + field = original_field if isinstance(original_field, FieldInfo) else (annotated_field or v) + # Get the original sqlmodel FieldInfo, pydantic >=v2.12 changes the model + field.annotation = v.annotation + # Guarantee the field has the correct type + col = get_column_from_field(field) setattr(new_cls, k, col) # Set a config flag to tell FastAPI that this should be read with a field # in orm_mode instead of preemptively converting it to a dict. From 93e15c1b88813e9bfa00525161769a788c8673a6 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 05:27:23 +0200 Subject: [PATCH 02/14] Add tests for syntax declaration --- tests/test_declaration_syntax.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_declaration_syntax.py diff --git a/tests/test_declaration_syntax.py b/tests/test_declaration_syntax.py new file mode 100644 index 0000000000..34bc92186b --- /dev/null +++ b/tests/test_declaration_syntax.py @@ -0,0 +1,18 @@ +from typing import Annotated + +from sqlmodel import SQLModel, Field + + +def test_declaration_syntax_1(): + class Person1(SQLModel, table=True): + name: str = Field(primary_key=True) + + +def test_declaration_syntax_2(): + class Person2(SQLModel, table=True): + name: Annotated[str, Field(primary_key=True)] + + +def test_declaration_syntax_3(): + class Person3(SQLModel, table=True): + name: Annotated[str, ...] = Field(primary_key=True) From 38dc566d1abb3ac121ea90708f68637970541705 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 05:37:55 +0200 Subject: [PATCH 03/14] Simplify logic --- sqlmodel/main.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 536ae48fcb..469ed9706a 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -563,12 +563,14 @@ def get_config(name: str) -> Any: set_config_value(model=new_cls, parameter="table", value=config_table) for k, v in get_model_fields(new_cls).items(): original_field = getattr(v, "_original_assignment", Undefined) - annotated_field_meta = new_cls.__annotations__[k].__dict__.get("__metadata__", []) - annotated_field = next((f for f in annotated_field_meta if isinstance(f, FieldInfo)), None) - field = original_field if isinstance(original_field, FieldInfo) else (annotated_field or v) # Get the original sqlmodel FieldInfo, pydantic >=v2.12 changes the model - field.annotation = v.annotation - # Guarantee the field has the correct type + if isinstance(original_field, FieldInfo): + field = original_field + else: + annotated_field_meta = new_cls.__annotations__[k].__dict__.get("__metadata__", []) + field = next((f for f in annotated_field_meta if isinstance(f, FieldInfo)), v) + field.annotation = v.annotation + # Guarantee the field has the correct type col = get_column_from_field(field) setattr(new_cls, k, col) # Set a config flag to tell FastAPI that this should be read with a field From 1b69033fbce85dc015f912887bd8d31103b8f9f1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 03:38:39 +0000 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20f?= =?UTF-8?q?ormat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 8 ++++++-- tests/test_declaration_syntax.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 469ed9706a..4fa9ea12da 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -567,8 +567,12 @@ def get_config(name: str) -> Any: if isinstance(original_field, FieldInfo): field = original_field else: - annotated_field_meta = new_cls.__annotations__[k].__dict__.get("__metadata__", []) - field = next((f for f in annotated_field_meta if isinstance(f, FieldInfo)), v) + annotated_field_meta = new_cls.__annotations__[k].__dict__.get( + "__metadata__", [] + ) + field = next( + (f for f in annotated_field_meta if isinstance(f, FieldInfo)), v + ) field.annotation = v.annotation # Guarantee the field has the correct type col = get_column_from_field(field) diff --git a/tests/test_declaration_syntax.py b/tests/test_declaration_syntax.py index 34bc92186b..c27bcadb81 100644 --- a/tests/test_declaration_syntax.py +++ b/tests/test_declaration_syntax.py @@ -1,6 +1,6 @@ from typing import Annotated -from sqlmodel import SQLModel, Field +from sqlmodel import Field, SQLModel def test_declaration_syntax_1(): From 46ce3122cdc5b27392834e496b317aebff95f620 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 06:02:29 +0200 Subject: [PATCH 05/14] Add type ignore comment for field assignment --- sqlmodel/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 4fa9ea12da..884ec9f0f0 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -572,7 +572,7 @@ def get_config(name: str) -> Any: ) field = next( (f for f in annotated_field_meta if isinstance(f, FieldInfo)), v - ) + ) # type: ignore[assignment] field.annotation = v.annotation # Guarantee the field has the correct type col = get_column_from_field(field) From cf62a1879649240a93063b10e9028220bc381890 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 06:05:25 +0200 Subject: [PATCH 06/14] Remove type ignore comments from execute method signatures --- sqlmodel/ext/asyncio/session.py | 2 +- sqlmodel/orm/session.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sqlmodel/ext/asyncio/session.py b/sqlmodel/ext/asyncio/session.py index 54488357bb..ff99dff899 100644 --- a/sqlmodel/ext/asyncio/session.py +++ b/sqlmodel/ext/asyncio/session.py @@ -129,7 +129,7 @@ async def exec( ``` """ ) - async def execute( # type: ignore + async def execute( self, statement: _Executable, params: Optional[_CoreAnyExecuteParams] = None, diff --git a/sqlmodel/orm/session.py b/sqlmodel/orm/session.py index dca4733d61..9e82d48a73 100644 --- a/sqlmodel/orm/session.py +++ b/sqlmodel/orm/session.py @@ -113,7 +113,7 @@ def exec( """, category=None, ) - def execute( # type: ignore + def execute( self, statement: _Executable, params: Optional[_CoreAnyExecuteParams] = None, From 7fd7f0cf03b4b86ad9699968edcd6aeee2eb3a19 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 14:26:18 +0200 Subject: [PATCH 07/14] Fix inheritance attrib not being properly parsed --- sqlmodel/main.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 884ec9f0f0..8a542e4279 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -567,14 +567,13 @@ def get_config(name: str) -> Any: if isinstance(original_field, FieldInfo): field = original_field else: - annotated_field_meta = new_cls.__annotations__[k].__dict__.get( - "__metadata__", [] - ) + annotated_field = get_annotations(new_cls.__dict__).get(k, {}) + annotated_field_meta = getattr(annotated_field,"__metadata__", (())) field = next( (f for f in annotated_field_meta if isinstance(f, FieldInfo)), v ) # type: ignore[assignment] - field.annotation = v.annotation - # Guarantee the field has the correct type + field.annotation = v.annotation + # Guarantee the field has the correct type col = get_column_from_field(field) setattr(new_cls, k, col) # Set a config flag to tell FastAPI that this should be read with a field From 28445d114b3e49a80bd5a8d93f9655f8b806a7ad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 12:26:32 +0000 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20f?= =?UTF-8?q?ormat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 8a542e4279..ccffd7aae9 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -568,7 +568,9 @@ def get_config(name: str) -> Any: field = original_field else: annotated_field = get_annotations(new_cls.__dict__).get(k, {}) - annotated_field_meta = getattr(annotated_field,"__metadata__", (())) + annotated_field_meta = getattr( + annotated_field, "__metadata__", (()) + ) field = next( (f for f in annotated_field_meta if isinstance(f, FieldInfo)), v ) # type: ignore[assignment] From b867ad76681ddaf5c4dd8b1050f5a3beec0be720 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 14:54:43 +0200 Subject: [PATCH 09/14] Fix inheritance when getting annotations --- sqlmodel/main.py | 6 +++++- tests/test_declaration_syntax.py | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index ccffd7aae9..b486e0a85f 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -567,7 +567,11 @@ def get_config(name: str) -> Any: if isinstance(original_field, FieldInfo): field = original_field else: - annotated_field = get_annotations(new_cls.__dict__).get(k, {}) + annotated_field = next( + ann + for c in new_cls.__mro__ + if (ann := get_annotations(c.__dict__).get(k)) + ) annotated_field_meta = getattr( annotated_field, "__metadata__", (()) ) diff --git a/tests/test_declaration_syntax.py b/tests/test_declaration_syntax.py index c27bcadb81..e1775d14ea 100644 --- a/tests/test_declaration_syntax.py +++ b/tests/test_declaration_syntax.py @@ -4,15 +4,24 @@ def test_declaration_syntax_1(): - class Person1(SQLModel, table=True): + class Person1(SQLModel): name: str = Field(primary_key=True) + class Person1Final(Person1, table=True): + pass + def test_declaration_syntax_2(): - class Person2(SQLModel, table=True): + class Person2(SQLModel): name: Annotated[str, Field(primary_key=True)] + class Person2Final(Person2, table=True): + pass + def test_declaration_syntax_3(): - class Person3(SQLModel, table=True): + class Person3(SQLModel): name: Annotated[str, ...] = Field(primary_key=True) + + class Person3Final(Person3, table=True): + pass From 68beb7271314b88590ca7f0fd525a2fcada288b4 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 14:57:09 +0200 Subject: [PATCH 10/14] Fix type ignore comment for annotation retrieval --- sqlmodel/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index b486e0a85f..d0f3f7b247 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -570,7 +570,7 @@ def get_config(name: str) -> Any: annotated_field = next( ann for c in new_cls.__mro__ - if (ann := get_annotations(c.__dict__).get(k)) + if (ann := get_annotations(c.__dict__).get(k)) # type: ignore[arg-type] ) annotated_field_meta = getattr( annotated_field, "__metadata__", (()) From 45af0114d0676130d48200677bfae35c71c351ef Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 12:57:21 +0000 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20f?= =?UTF-8?q?ormat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index d0f3f7b247..920ed1c74a 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -570,7 +570,7 @@ def get_config(name: str) -> Any: annotated_field = next( ann for c in new_cls.__mro__ - if (ann := get_annotations(c.__dict__).get(k)) # type: ignore[arg-type] + if (ann := get_annotations(c.__dict__).get(k)) # type: ignore[arg-type] ) annotated_field_meta = getattr( annotated_field, "__metadata__", (()) From b50566d1847e6584f9234650e9ec959299b6b52e Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 15:11:27 +0200 Subject: [PATCH 12/14] Fix col form field for pydantic 1 --- sqlmodel/main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 920ed1c74a..00a626158f 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -562,6 +562,11 @@ def get_config(name: str) -> Any: # If it was passed by kwargs, ensure it's also set in config set_config_value(model=new_cls, parameter="table", value=config_table) for k, v in get_model_fields(new_cls).items(): + if not IS_PYDANTIC_V2: + col = get_column_from_field(v) + setattr(new_cls, k, col) + continue + original_field = getattr(v, "_original_assignment", Undefined) # Get the original sqlmodel FieldInfo, pydantic >=v2.12 changes the model if isinstance(original_field, FieldInfo): @@ -576,7 +581,8 @@ def get_config(name: str) -> Any: annotated_field, "__metadata__", (()) ) field = next( - (f for f in annotated_field_meta if isinstance(f, FieldInfo)), v + (f for f in annotated_field_meta if isinstance(f, FieldInfo)), + v, ) # type: ignore[assignment] field.annotation = v.annotation # Guarantee the field has the correct type From 6fd5534f6b13f708718ed544e7fa9083766ddb48 Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Sat, 11 Oct 2025 15:15:05 +0200 Subject: [PATCH 13/14] Fix import --- tests/test_declaration_syntax.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_declaration_syntax.py b/tests/test_declaration_syntax.py index e1775d14ea..227ce73ac4 100644 --- a/tests/test_declaration_syntax.py +++ b/tests/test_declaration_syntax.py @@ -1,4 +1,4 @@ -from typing import Annotated +from typing_extensions import Annotated from sqlmodel import Field, SQLModel From ee8fd7bb8167a52c1dd385545be927b49c869626 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 13:15:16 +0000 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20f?= =?UTF-8?q?ormat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_declaration_syntax.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_declaration_syntax.py b/tests/test_declaration_syntax.py index 227ce73ac4..803cb9f0fd 100644 --- a/tests/test_declaration_syntax.py +++ b/tests/test_declaration_syntax.py @@ -1,6 +1,5 @@ -from typing_extensions import Annotated - from sqlmodel import Field, SQLModel +from typing_extensions import Annotated def test_declaration_syntax_1():