Coverage for tests / test_main.py: 96%
152 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-06-10 09:40 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-06-10 09:40 +0000
1from typing import Annotated 1456789!#
3import pytest 1456789!#
4from sqlalchemy.exc import IntegrityError 1456789!#
5from sqlalchemy.orm import RelationshipProperty 1456789!#
6from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select 1456789!#
9def test_should_allow_duplicate_row_if_unique_constraint_is_not_passed(clear_sqlmodel): 1456789!#
10 class Hero(SQLModel, table=True): 1abcdefgh
11 id: int | None = Field(default=None, primary_key=True) 1abcdefgh
12 name: str 1abcdefgh
13 secret_name: str 1abcdefgh
14 age: int | None = None 1abcdefgh
16 hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") 1abcdefgh
17 hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson") 1abcdefgh
19 engine = create_engine("sqlite://") 1abcdefgh
21 SQLModel.metadata.create_all(engine) 1abcdefgh
23 with Session(engine) as session: 1abcdefgh
24 session.add(hero_1) 1abcdefgh
25 session.commit() 1abcdefgh
26 session.refresh(hero_1) 1abcdefgh
28 with Session(engine) as session: 1abcdefgh
29 session.add(hero_2) 1abcdefgh
30 session.commit() 1abcdefgh
31 session.refresh(hero_2) 1abcdefgh
33 with Session(engine) as session: 1abcdefgh
34 heroes = session.exec(select(Hero)).all() 1abcdefgh
35 assert len(heroes) == 2 1abcdefgh
36 assert heroes[0].name == heroes[1].name 1abcdefgh
39def test_should_allow_duplicate_row_if_unique_constraint_is_false(clear_sqlmodel): 1456789!#
40 class Hero(SQLModel, table=True): 1ijklmnop
41 id: int | None = Field(default=None, primary_key=True) 1ijklmnop
42 name: str 1ijklmnop
43 secret_name: str = Field(unique=False) 1ijklmnop
44 age: int | None = None 1ijklmnop
46 hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") 1ijklmnop
47 hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson") 1ijklmnop
49 engine = create_engine("sqlite://") 1ijklmnop
51 SQLModel.metadata.create_all(engine) 1ijklmnop
53 with Session(engine) as session: 1ijklmnop
54 session.add(hero_1) 1ijklmnop
55 session.commit() 1ijklmnop
56 session.refresh(hero_1) 1ijklmnop
58 with Session(engine) as session: 1ijklmnop
59 session.add(hero_2) 1ijklmnop
60 session.commit() 1ijklmnop
61 session.refresh(hero_2) 1ijklmnop
63 with Session(engine) as session: 1ijklmnop
64 heroes = session.exec(select(Hero)).all() 1ijklmnop
65 assert len(heroes) == 2 1ijklmnop
66 assert heroes[0].name == heroes[1].name 1ijklmnop
69def test_should_raise_exception_when_try_to_duplicate_row_if_unique_constraint_is_true( 1456789!#
70 clear_sqlmodel,
71):
72 class Hero(SQLModel, table=True): 1OPQRSTUV
73 id: int | None = Field(default=None, primary_key=True) 1OPQRSTUV
74 name: str 1OPQRSTUV
75 secret_name: str = Field(unique=True) 1OPQRSTUV
76 age: int | None = None 1OPQRSTUV
78 hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") 1OPQRSTUV
79 hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson") 1OPQRSTUV
81 engine = create_engine("sqlite://") 1OPQRSTUV
83 SQLModel.metadata.create_all(engine) 1OPQRSTUV
85 with Session(engine) as session: 1OPQRSTUV
86 session.add(hero_1) 1OPQRSTUV
87 session.commit() 1OPQRSTUV
88 session.refresh(hero_1) 1OPQRSTUV
90 with pytest.raises(IntegrityError): 1OPQRSTUV
91 with Session(engine) as session: 1OPQRSTUV
92 session.add(hero_2) 1OPQRSTUV
93 session.commit() 1OPQRSTUV
96def test_sa_relationship_property(clear_sqlmodel): 1456789!#
97 """Test https://github.com/tiangolo/sqlmodel/issues/315#issuecomment-1272122306"""
99 class Team(SQLModel, table=True): 1yzABCDEF
100 id: int | None = Field(default=None, primary_key=True) 1yzABCDEF
101 name: str = Field(unique=True) 1yzABCDEF
102 heroes: list["Hero"] = Relationship( # noqa: F821 1yzABCDEF
103 sa_relationship=RelationshipProperty("Hero", back_populates="team")
104 )
106 class Hero(SQLModel, table=True): 1yzABCDEF
107 id: int | None = Field(default=None, primary_key=True) 1yzABCDEF
108 name: str = Field(unique=True) 1yzABCDEF
109 team_id: int | None = Field(default=None, foreign_key="team.id") 1yzABCDEF
110 team: Team | None = Relationship( 1yzABCDEF
111 sa_relationship=RelationshipProperty("Team", back_populates="heroes")
112 )
114 team_preventers = Team(name="Preventers") 1yzABCDEF
115 hero_rusty_man = Hero(name="Rusty-Man", team=team_preventers) 1yzABCDEF
117 engine = create_engine("sqlite://", echo=True) 1yzABCDEF
119 SQLModel.metadata.create_all(engine) 1yzABCDEF
121 with Session(engine) as session: 1yzABCDEF
122 session.add(hero_rusty_man) 1yzABCDEF
123 session.commit() 1yzABCDEF
124 session.refresh(hero_rusty_man) 1yzABCDEF
125 # The next statement should not raise an AttributeError
126 assert hero_rusty_man.team 1yzABCDEF
127 assert hero_rusty_man.team.name == "Preventers" 1yzABCDEF
130def test_composite_primary_key(clear_sqlmodel): 1456789!#
131 class UserPermission(SQLModel, table=True): 1GHIJKLMN
132 user_id: int = Field(primary_key=True) 1GHIJKLMN
133 resource_id: int = Field(primary_key=True) 1GHIJKLMN
134 permission: str 1GHIJKLMN
136 engine = create_engine("sqlite://") 1GHIJKLMN
137 SQLModel.metadata.create_all(engine) 1GHIJKLMN
139 pk_column_names = {column.name for column in UserPermission.__table__.primary_key} 1GHIJKLMN
140 assert pk_column_names == {"user_id", "resource_id"} 1GHIJKLMN
142 with Session(engine) as session: 1GHIJKLMN
143 perm1 = UserPermission(user_id=1, resource_id=1, permission="read") 1GHIJKLMN
144 perm2 = UserPermission(user_id=1, resource_id=2, permission="write") 1GHIJKLMN
145 session.add(perm1) 1GHIJKLMN
146 session.add(perm2) 1GHIJKLMN
147 session.commit() 1GHIJKLMN
149 with pytest.raises(IntegrityError): 1GHIJKLMN
150 with Session(engine) as session: 1GHIJKLMN
151 perm3 = UserPermission(user_id=1, resource_id=1, permission="admin") 1GHIJKLMN
152 session.add(perm3) 1GHIJKLMN
153 session.commit() 1GHIJKLMN
156def test_composite_primary_key_and_validator(clear_sqlmodel): 1456789!#
157 from pydantic import AfterValidator 1qrstuvwx
159 def validate_resource_id(value: int) -> int: 1qrstuvwx
160 if value < 1:
161 raise ValueError("Resource ID must be positive")
162 return value
164 class UserPermission(SQLModel, table=True): 1qrstuvwx
165 user_id: int = Field(primary_key=True) 1qrstuvwx
166 resource_id: Annotated[int, AfterValidator(validate_resource_id)] = Field( 1qrstuvwx
167 primary_key=True
168 )
169 permission: str 1qrstuvwx
171 engine = create_engine("sqlite://") 1qrstuvwx
172 SQLModel.metadata.create_all(engine) 1qrstuvwx
174 pk_column_names = {column.name for column in UserPermission.__table__.primary_key} 1qrstuvwx
175 assert pk_column_names == {"user_id", "resource_id"} 1qrstuvwx
177 with Session(engine) as session: 1qrstuvwx
178 perm1 = UserPermission(user_id=1, resource_id=1, permission="read") 1qrstuvwx
179 perm2 = UserPermission(user_id=1, resource_id=2, permission="write") 1qrstuvwx
180 session.add(perm1) 1qrstuvwx
181 session.add(perm2) 1qrstuvwx
182 session.commit() 1qrstuvwx
184 with pytest.raises(IntegrityError): 1qrstuvwx
185 with Session(engine) as session: 1qrstuvwx
186 perm3 = UserPermission(user_id=1, resource_id=1, permission="admin") 1qrstuvwx
187 session.add(perm3) 1qrstuvwx
188 session.commit() 1qrstuvwx
191def test_foreign_key_ondelete_with_annotated(clear_sqlmodel): 1456789!#
192 from pydantic import AfterValidator 1WXYZ0123
194 def ensure_positive(value: int) -> int: 1WXYZ0123
195 if value < 0:
196 raise ValueError("Team ID must be positive")
197 return value
199 class Team(SQLModel, table=True): 1WXYZ0123
200 id: int = Field(primary_key=True) 1WXYZ0123
201 name: str 1WXYZ0123
203 class Hero(SQLModel, table=True): 1WXYZ0123
204 id: int = Field(primary_key=True) 1WXYZ0123
205 team_id: Annotated[int, AfterValidator(ensure_positive)] = Field( 1WXYZ0123
206 foreign_key="team.id",
207 ondelete="CASCADE",
208 )
209 name: str 1WXYZ0123
211 engine = create_engine("sqlite://") 1WXYZ0123
212 SQLModel.metadata.create_all(engine) 1WXYZ0123
214 team_id_column = Hero.__table__.c.team_id # type: ignore[attr-defined] 1WXYZ0123
215 foreign_keys = list(team_id_column.foreign_keys) 1WXYZ0123
216 assert len(foreign_keys) == 1 1WXYZ0123
217 assert foreign_keys[0].ondelete == "CASCADE" 1WXYZ0123
218 assert team_id_column.nullable is False 1WXYZ0123