Skip to content

Commit 98c63d4

Browse files
committed
Add test suite for polymorphic inheritance
1 parent c2851b2 commit 98c63d4

11 files changed

Lines changed: 1901 additions & 0 deletions
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""Mirrors sqlalchemy/test/orm/inheritance/test_basic.py :: FalseDiscriminatorTest, CascadeTest"""
2+
3+
from typing import Optional
4+
5+
from sqlalchemy import Boolean
6+
from sqlalchemy.orm import mapped_column
7+
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select
8+
9+
10+
def test_false_discriminator_on_sub():
11+
# mirrors FalseDiscriminatorTest.test_false_on_sub
12+
class DFoo(SQLModel, table=True):
13+
__tablename__ = "t_false_sub"
14+
id: Optional[int] = Field(default=None, primary_key=True)
15+
type: Optional[bool] = Field(
16+
default=None, sa_column=mapped_column(Boolean, nullable=True)
17+
)
18+
19+
__mapper_args__ = {"polymorphic_on": "type", "polymorphic_identity": True}
20+
21+
class DBar(DFoo):
22+
__mapper_args__ = {"polymorphic_identity": False}
23+
24+
engine = create_engine("sqlite:///:memory:")
25+
SQLModel.metadata.create_all(engine)
26+
27+
with Session(engine) as db:
28+
b1 = DBar()
29+
db.add(b1)
30+
db.commit()
31+
db.refresh(b1)
32+
assert b1.type is False
33+
34+
with Session(engine) as db:
35+
result = db.exec(select(DFoo)).one()
36+
assert isinstance(result, DBar)
37+
38+
39+
def test_false_discriminator_on_base():
40+
# mirrors FalseDiscriminatorTest.test_false_on_base
41+
class Ding(SQLModel, table=True):
42+
__tablename__ = "t_false_base"
43+
id: Optional[int] = Field(default=None, primary_key=True)
44+
type: Optional[bool] = Field(
45+
default=None, sa_column=mapped_column(Boolean, nullable=True)
46+
)
47+
48+
__mapper_args__ = {"polymorphic_on": "type", "polymorphic_identity": False}
49+
50+
class Bat(Ding):
51+
__mapper_args__ = {"polymorphic_identity": True}
52+
53+
engine = create_engine("sqlite:///:memory:")
54+
SQLModel.metadata.create_all(engine)
55+
56+
with Session(engine) as db:
57+
d1 = Ding()
58+
db.add(d1)
59+
db.commit()
60+
db.refresh(d1)
61+
assert d1.type is False
62+
63+
with Session(engine) as db:
64+
assert db.exec(select(Ding)).one() is not None
65+
66+
67+
def test_cascade_delete_follows_subclass_mapper():
68+
# mirrors CascadeTest.test_cascade
69+
class Note(SQLModel, table=True):
70+
__tablename__ = "cascade_note"
71+
id: Optional[int] = Field(default=None, primary_key=True)
72+
data: str
73+
item_id: Optional[int] = Field(default=None, foreign_key="cascade_item.id")
74+
75+
class Item(SQLModel, table=True):
76+
__tablename__ = "cascade_item"
77+
id: Optional[int] = Field(default=None, primary_key=True)
78+
type: str = Field(default="item")
79+
data: str
80+
notes: list[Note] = Relationship(sa_relationship_kwargs={"cascade": "all"})
81+
82+
__mapper_args__ = {
83+
"polymorphic_on": "type",
84+
"polymorphic_identity": "item",
85+
}
86+
87+
class SubItem(Item):
88+
__tablename__ = "cascade_subitem"
89+
id: Optional[int] = Field(
90+
default=None, primary_key=True, foreign_key="cascade_item.id"
91+
)
92+
extra: str = Field(default="")
93+
94+
__mapper_args__ = {"polymorphic_identity": "subitem"}
95+
96+
engine = create_engine("sqlite:///:memory:")
97+
SQLModel.metadata.create_all(engine)
98+
99+
with Session(engine) as db:
100+
sub = SubItem(data="hello", extra="world")
101+
db.add(sub)
102+
db.flush()
103+
sub_id = sub.id
104+
db.add(Note(data="note1", item_id=sub_id))
105+
db.add(Note(data="note2", item_id=sub_id))
106+
db.commit()
107+
108+
with Session(engine) as db:
109+
sub = db.get(SubItem, sub_id)
110+
_ = sub.notes
111+
db.delete(sub)
112+
db.commit()
113+
114+
with Session(engine) as db:
115+
assert db.exec(select(Note)).all() == []
116+
assert db.exec(select(Item)).all() == []
117+
118+
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"""Mirrors sqlalchemy/test/orm/inheritance/test_concrete.py :: ConcreteTest, PropertyInheritanceTest"""
2+
3+
from typing import Optional
4+
5+
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select
6+
7+
8+
def test_basic():
9+
# mirrors ConcreteTest.test_basic
10+
class Manager(SQLModel, table=True):
11+
__tablename__ = "cti_managers"
12+
employee_id: Optional[int] = Field(default=None, primary_key=True)
13+
name: str
14+
manager_data: str
15+
16+
class Engineer(SQLModel, table=True):
17+
__tablename__ = "cti_engineers"
18+
employee_id: Optional[int] = Field(default=None, primary_key=True)
19+
name: str
20+
engineer_info: str
21+
22+
engine = create_engine("sqlite:///:memory:")
23+
SQLModel.metadata.create_all(engine)
24+
25+
with Session(engine) as db:
26+
db.add(Manager(name="Sally", manager_data="knows how to manage things"))
27+
db.add(Engineer(name="Karina", engineer_info="knows how to hack"))
28+
db.commit()
29+
30+
with Session(engine) as db:
31+
managers = db.exec(select(Manager)).all()
32+
engineers = db.exec(select(Engineer)).all()
33+
34+
db.expire(managers[0], ["manager_data"])
35+
assert managers[0].manager_data == "knows how to manage things"
36+
37+
assert {m.name for m in managers} == {"Sally"}
38+
assert {e.name for e in engineers} == {"Karina"}
39+
40+
41+
def test_multi_level_no_base():
42+
# mirrors ConcreteTest.test_multi_level_no_base
43+
class Manager(SQLModel, table=True):
44+
__tablename__ = "cti3_managers"
45+
employee_id: Optional[int] = Field(default=None, primary_key=True)
46+
name: str
47+
manager_data: str
48+
49+
class Engineer(SQLModel, table=True):
50+
__tablename__ = "cti3_engineers"
51+
employee_id: Optional[int] = Field(default=None, primary_key=True)
52+
name: str
53+
engineer_info: str
54+
55+
class Hacker(SQLModel, table=True):
56+
__tablename__ = "cti3_hackers"
57+
employee_id: Optional[int] = Field(default=None, primary_key=True)
58+
name: str
59+
engineer_info: str
60+
nickname: str
61+
62+
engine = create_engine("sqlite:///:memory:")
63+
SQLModel.metadata.create_all(engine)
64+
65+
with Session(engine) as db:
66+
db.add(Manager(name="Sally", manager_data="knows how to manage things"))
67+
db.add(Engineer(name="Jenn", engineer_info="knows how to program"))
68+
db.add(Hacker(name="Karina", engineer_info="knows how to hack", nickname="Badass"))
69+
db.commit()
70+
71+
with Session(engine) as db:
72+
managers = db.exec(select(Manager)).all()
73+
engineers = db.exec(select(Engineer)).all()
74+
hackers = db.exec(select(Hacker)).all()
75+
76+
assert len(managers) == 1 and managers[0].name == "Sally"
77+
assert len(engineers) == 1 and engineers[0].engineer_info == "knows how to program"
78+
assert len(hackers) == 1 and hackers[0].nickname == "Badass"
79+
80+
with Session(engine) as db:
81+
assert db.exec(select(Manager).where(Manager.name == "Karina")).first() is None
82+
83+
84+
def test_relationship():
85+
# mirrors ConcreteTest.test_relationship / PropertyInheritanceTest
86+
class Company(SQLModel, table=True):
87+
__tablename__ = "cti4_companies"
88+
id: Optional[int] = Field(default=None, primary_key=True)
89+
name: str
90+
managers: list["CtiManager"] = Relationship(back_populates="company")
91+
engineers: list["CtiEngineer"] = Relationship(back_populates="company")
92+
93+
class CtiManager(SQLModel, table=True):
94+
__tablename__ = "cti4_managers"
95+
employee_id: Optional[int] = Field(default=None, primary_key=True)
96+
name: str
97+
manager_data: str
98+
company_id: Optional[int] = Field(default=None, foreign_key="cti4_companies.id")
99+
company: Optional[Company] = Relationship(back_populates="managers")
100+
101+
class CtiEngineer(SQLModel, table=True):
102+
__tablename__ = "cti4_engineers"
103+
employee_id: Optional[int] = Field(default=None, primary_key=True)
104+
name: str
105+
engineer_info: str
106+
company_id: Optional[int] = Field(default=None, foreign_key="cti4_companies.id")
107+
company: Optional[Company] = Relationship(back_populates="engineers")
108+
109+
engine = create_engine("sqlite:///:memory:")
110+
SQLModel.metadata.create_all(engine)
111+
112+
with Session(engine) as db:
113+
corp = Company(name="Initech")
114+
db.add(corp)
115+
db.flush()
116+
cid = corp.id
117+
db.add(CtiManager(name="Bill", manager_data="TPS reports", company_id=cid))
118+
db.add(CtiEngineer(name="Peter", engineer_info="cubicle dweller", company_id=cid))
119+
db.commit()
120+
121+
with Session(engine) as db:
122+
corp = db.get(Company, cid)
123+
assert len(corp.managers) == 1 and corp.managers[0].name == "Bill"
124+
assert len(corp.engineers) == 1 and corp.engineers[0].name == "Peter"

0 commit comments

Comments
 (0)