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

1from typing import Annotated 1456789!#

2 

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!#

7 

8 

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

15 

16 hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") 1abcdefgh

17 hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson") 1abcdefgh

18 

19 engine = create_engine("sqlite://") 1abcdefgh

20 

21 SQLModel.metadata.create_all(engine) 1abcdefgh

22 

23 with Session(engine) as session: 1abcdefgh

24 session.add(hero_1) 1abcdefgh

25 session.commit() 1abcdefgh

26 session.refresh(hero_1) 1abcdefgh

27 

28 with Session(engine) as session: 1abcdefgh

29 session.add(hero_2) 1abcdefgh

30 session.commit() 1abcdefgh

31 session.refresh(hero_2) 1abcdefgh

32 

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

37 

38 

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

45 

46 hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") 1ijklmnop

47 hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson") 1ijklmnop

48 

49 engine = create_engine("sqlite://") 1ijklmnop

50 

51 SQLModel.metadata.create_all(engine) 1ijklmnop

52 

53 with Session(engine) as session: 1ijklmnop

54 session.add(hero_1) 1ijklmnop

55 session.commit() 1ijklmnop

56 session.refresh(hero_1) 1ijklmnop

57 

58 with Session(engine) as session: 1ijklmnop

59 session.add(hero_2) 1ijklmnop

60 session.commit() 1ijklmnop

61 session.refresh(hero_2) 1ijklmnop

62 

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

67 

68 

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

77 

78 hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") 1OPQRSTUV

79 hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson") 1OPQRSTUV

80 

81 engine = create_engine("sqlite://") 1OPQRSTUV

82 

83 SQLModel.metadata.create_all(engine) 1OPQRSTUV

84 

85 with Session(engine) as session: 1OPQRSTUV

86 session.add(hero_1) 1OPQRSTUV

87 session.commit() 1OPQRSTUV

88 session.refresh(hero_1) 1OPQRSTUV

89 

90 with pytest.raises(IntegrityError): 1OPQRSTUV

91 with Session(engine) as session: 1OPQRSTUV

92 session.add(hero_2) 1OPQRSTUV

93 session.commit() 1OPQRSTUV

94 

95 

96def test_sa_relationship_property(clear_sqlmodel): 1456789!#

97 """Test https://github.com/tiangolo/sqlmodel/issues/315#issuecomment-1272122306""" 

98 

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 ) 

105 

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 ) 

113 

114 team_preventers = Team(name="Preventers") 1yzABCDEF

115 hero_rusty_man = Hero(name="Rusty-Man", team=team_preventers) 1yzABCDEF

116 

117 engine = create_engine("sqlite://", echo=True) 1yzABCDEF

118 

119 SQLModel.metadata.create_all(engine) 1yzABCDEF

120 

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

128 

129 

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

135 

136 engine = create_engine("sqlite://") 1GHIJKLMN

137 SQLModel.metadata.create_all(engine) 1GHIJKLMN

138 

139 pk_column_names = {column.name for column in UserPermission.__table__.primary_key} 1GHIJKLMN

140 assert pk_column_names == {"user_id", "resource_id"} 1GHIJKLMN

141 

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

148 

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

154 

155 

156def test_composite_primary_key_and_validator(clear_sqlmodel): 1456789!#

157 from pydantic import AfterValidator 1qrstuvwx

158 

159 def validate_resource_id(value: int) -> int: 1qrstuvwx

160 if value < 1: 

161 raise ValueError("Resource ID must be positive") 

162 return value 

163 

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

170 

171 engine = create_engine("sqlite://") 1qrstuvwx

172 SQLModel.metadata.create_all(engine) 1qrstuvwx

173 

174 pk_column_names = {column.name for column in UserPermission.__table__.primary_key} 1qrstuvwx

175 assert pk_column_names == {"user_id", "resource_id"} 1qrstuvwx

176 

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

183 

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

189 

190 

191def test_foreign_key_ondelete_with_annotated(clear_sqlmodel): 1456789!#

192 from pydantic import AfterValidator 1WXYZ0123

193 

194 def ensure_positive(value: int) -> int: 1WXYZ0123

195 if value < 0: 

196 raise ValueError("Team ID must be positive") 

197 return value 

198 

199 class Team(SQLModel, table=True): 1WXYZ0123

200 id: int = Field(primary_key=True) 1WXYZ0123

201 name: str 1WXYZ0123

202 

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

210 

211 engine = create_engine("sqlite://") 1WXYZ0123

212 SQLModel.metadata.create_all(engine) 1WXYZ0123

213 

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