Skip to content

Commit 25d3e9c

Browse files
committed
🐛 Fix composite primary with AfterValidator/Annotated
1 parent a85de91 commit 25d3e9c

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

tests/test_main.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from typing import List, Optional
22

33
import pytest
4+
from sqlalchemy import inspect
5+
from sqlalchemy.engine.reflection import Inspector
46
from sqlalchemy.exc import IntegrityError
57
from sqlalchemy.orm import RelationshipProperty
68
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select
9+
from typing_extensions import Annotated
10+
11+
from .conftest import needs_pydanticv2
712

813

914
def test_should_allow_duplicate_row_if_unique_constraint_is_not_passed(clear_sqlmodel):
@@ -125,3 +130,73 @@ class Hero(SQLModel, table=True):
125130
# The next statement should not raise an AttributeError
126131
assert hero_rusty_man.team
127132
assert hero_rusty_man.team.name == "Preventers"
133+
134+
135+
def test_composite_primary_key(clear_sqlmodel):
136+
class UserPermission(SQLModel, table=True):
137+
user_id: int = Field(primary_key=True)
138+
resource_id: int = Field(primary_key=True)
139+
permission: str
140+
141+
engine = create_engine("sqlite://")
142+
SQLModel.metadata.create_all(engine)
143+
144+
insp: Inspector = inspect(engine)
145+
pk_constraint = insp.get_pk_constraint(str(UserPermission.__tablename__))
146+
147+
assert len(pk_constraint["constrained_columns"]) == 2
148+
assert "user_id" in pk_constraint["constrained_columns"]
149+
assert "resource_id" in pk_constraint["constrained_columns"]
150+
151+
with Session(engine) as session:
152+
perm1 = UserPermission(user_id=1, resource_id=1, permission="read")
153+
perm2 = UserPermission(user_id=1, resource_id=2, permission="write")
154+
session.add(perm1)
155+
session.add(perm2)
156+
session.commit()
157+
158+
with pytest.raises(IntegrityError):
159+
with Session(engine) as session:
160+
perm3 = UserPermission(user_id=1, resource_id=1, permission="admin")
161+
session.add(perm3)
162+
session.commit()
163+
164+
165+
@needs_pydanticv2
166+
def test_composite_primary_key_and_validator(clear_sqlmodel):
167+
from pydantic import AfterValidator
168+
169+
def validate_resource_id(value: int) -> int:
170+
if value < 1:
171+
raise ValueError("Resource ID must be positive")
172+
return value
173+
174+
class UserPermission(SQLModel, table=True):
175+
user_id: int = Field(primary_key=True)
176+
resource_id: Annotated[int, AfterValidator(validate_resource_id)] = Field(
177+
primary_key=True
178+
)
179+
permission: str
180+
181+
engine = create_engine("sqlite://")
182+
SQLModel.metadata.create_all(engine)
183+
184+
insp: Inspector = inspect(engine)
185+
pk_constraint = insp.get_pk_constraint(str(UserPermission.__tablename__))
186+
187+
assert len(pk_constraint["constrained_columns"]) == 2
188+
assert "user_id" in pk_constraint["constrained_columns"]
189+
assert "resource_id" in pk_constraint["constrained_columns"]
190+
191+
with Session(engine) as session:
192+
perm1 = UserPermission(user_id=1, resource_id=1, permission="read")
193+
perm2 = UserPermission(user_id=1, resource_id=2, permission="write")
194+
session.add(perm1)
195+
session.add(perm2)
196+
session.commit()
197+
198+
with pytest.raises(IntegrityError):
199+
with Session(engine) as session:
200+
perm3 = UserPermission(user_id=1, resource_id=1, permission="admin")
201+
session.add(perm3)
202+
session.commit()

0 commit comments

Comments
 (0)