Skip to content

Commit 4b51f3c

Browse files
author
mon
committed
✏️ Fix pydantic invalid when table=True(#1036)
1 parent 5e7f84c commit 4b51f3c

File tree

3 files changed

+43
-22
lines changed

3 files changed

+43
-22
lines changed

sqlmodel/_compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,16 @@ def sqlmodel_init(*, self: "SQLModel", data: Dict[str, Any]) -> None:
346346
self_instance=self,
347347
)
348348
else:
349+
raw_self = self.model_copy()
350+
pydantic_validated_model = self.__pydantic_validator__.validate_python(
351+
data,
352+
self_instance=raw_self,
353+
)
354+
pydantic_dict = pydantic_validated_model.model_dump()
355+
for k in pydantic_dict.keys():
356+
if k not in data.keys():
357+
continue
358+
data[k] = pydantic_dict[k]
349359
sqlmodel_table_construct(
350360
self_instance=self,
351361
values=data,

tests/test_instance_no_args.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,7 @@
22

33
import pytest
44
from pydantic import ValidationError
5-
from sqlmodel import Field, Session, SQLModel, create_engine, select
6-
7-
8-
def test_allow_instantiation_without_arguments(clear_sqlmodel):
9-
class Item(SQLModel, table=True):
10-
id: Optional[int] = Field(default=None, primary_key=True)
11-
name: str
12-
description: Optional[str] = None
13-
14-
engine = create_engine("sqlite:///:memory:")
15-
SQLModel.metadata.create_all(engine)
16-
with Session(engine) as db:
17-
item = Item()
18-
item.name = "Rick"
19-
db.add(item)
20-
db.commit()
21-
statement = select(Item)
22-
result = db.exec(statement).all()
23-
assert len(result) == 1
24-
assert isinstance(item.id, int)
25-
SQLModel.metadata.clear()
5+
from sqlmodel import Field, SQLModel
266

277

288
def test_not_allow_instantiation_without_arguments_if_not_table():

tests/test_validation.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44
from pydantic.error_wrappers import ValidationError
5-
from sqlmodel import SQLModel
5+
from sqlmodel import Field, SQLModel
66

77
from .conftest import needs_pydanticv1, needs_pydanticv2
88

@@ -63,3 +63,34 @@ def reject_none(cls, v):
6363

6464
with pytest.raises(ValidationError):
6565
Hero.model_validate({"name": None, "age": 25})
66+
67+
68+
@needs_pydanticv2
69+
def test_validation_with_table_true():
70+
"""Test validation with table=True."""
71+
from pydantic import field_validator
72+
73+
class Hero(SQLModel, table=True):
74+
name: Optional[str] = Field(default=None, primary_key=True)
75+
secret_name: Optional[str] = None
76+
age: Optional[int] = None
77+
78+
@field_validator("age", mode="after")
79+
@classmethod
80+
def double_age(cls, v):
81+
if v is not None:
82+
return v * 2
83+
return v
84+
85+
Hero(name="Deadpond", age=25)
86+
Hero.model_validate({"name": "Deadpond", "age": 25})
87+
with pytest.raises(ValidationError):
88+
Hero(name="Deadpond", secret_name="Dive Wilson", age="test")
89+
with pytest.raises(ValidationError):
90+
Hero.model_validate({"name": "Deadpond", "age": "test"})
91+
92+
double_age_hero = Hero(name="Deadpond", age=25)
93+
assert double_age_hero.age == 50
94+
95+
double_age_hero = Hero.model_validate({"name": "Deadpond", "age": 25})
96+
assert double_age_hero.age == 50

0 commit comments

Comments
 (0)