@@ -175,3 +175,110 @@ class Worker(Person):
175175 result = db .exec (statement ).all ()
176176 assert len (result ) == 1
177177 assert isinstance (result [0 ].tool , Tool )
178+
179+
180+ @needs_pydanticv2
181+ def test_polymorphic_deeper (clear_sqlmodel ) -> None :
182+ class Employee (SQLModel , table = True ):
183+ __tablename__ = "employee"
184+
185+ id : Optional [int ] = Field (default = None , primary_key = True )
186+ name : str
187+ type : str = Field (default = "employee" )
188+
189+ __mapper_args__ = {
190+ "polymorphic_identity" : "employee" ,
191+ "polymorphic_on" : "type" ,
192+ }
193+
194+ class Executive (Employee ):
195+ """An executive of the company"""
196+
197+ executive_background : Optional [str ] = Field (
198+ sa_column = mapped_column (nullable = True ), default = None
199+ )
200+
201+ __mapper_args__ = {"polymorphic_abstract" : True }
202+
203+ class Technologist (Employee ):
204+ """An employee who works with technology"""
205+
206+ competencies : Optional [str ] = Field (
207+ sa_column = mapped_column (nullable = True ), default = None
208+ )
209+
210+ __mapper_args__ = {"polymorphic_abstract" : True }
211+
212+ class Manager (Executive ):
213+ """A manager"""
214+
215+ __mapper_args__ = {"polymorphic_identity" : "manager" }
216+
217+ class Principal (Executive ):
218+ """A principal of the company"""
219+
220+ __mapper_args__ = {"polymorphic_identity" : "principal" }
221+
222+ class Engineer (Technologist ):
223+ """An engineer"""
224+
225+ __mapper_args__ = {"polymorphic_identity" : "engineer" }
226+
227+ class SysAdmin (Technologist ):
228+ """A systems administrator"""
229+
230+ __mapper_args__ = {"polymorphic_identity" : "sysadmin" }
231+
232+ # Create database and session
233+ engine = create_engine ("sqlite:///:memory:" , echo = True )
234+ SQLModel .metadata .create_all (engine )
235+
236+ with Session (engine ) as db :
237+ # Add different employee types
238+ manager = Manager (name = "Alice" , executive_background = "MBA" )
239+ principal = Principal (name = "Bob" , executive_background = "Founder" )
240+ engineer = Engineer (name = "Charlie" , competencies = "Python, SQL" )
241+ sysadmin = SysAdmin (name = "Diana" , competencies = "Linux, Networking" )
242+
243+ db .add (manager )
244+ db .add (principal )
245+ db .add (engineer )
246+ db .add (sysadmin )
247+ db .commit ()
248+
249+ # Query each type to verify they persist correctly
250+ managers = db .exec (select (Manager )).all ()
251+ principals = db .exec (select (Principal )).all ()
252+ engineers = db .exec (select (Engineer )).all ()
253+ sysadmins = db .exec (select (SysAdmin )).all ()
254+
255+ # Query abstract classes to verify they return appropriate concrete classes
256+ executives = db .exec (select (Executive )).all ()
257+ technologists = db .exec (select (Technologist )).all ()
258+
259+ # All employees
260+ all_employees = db .exec (select (Employee )).all ()
261+
262+ # Assert individual type counts
263+ assert len (managers ) == 1
264+ assert len (principals ) == 1
265+ assert len (engineers ) == 1
266+ assert len (sysadmins ) == 1
267+
268+ # Check that abstract classes can't be instantiated directly
269+ # but their subclasses are correctly returned when querying
270+ assert len (executives ) == 2
271+ assert len (technologists ) == 2
272+ assert len (all_employees ) == 4
273+
274+ # Check that properties of abstract classes are accessible from concrete instances
275+ assert managers [0 ].executive_background == "MBA"
276+ assert principals [0 ].executive_background == "Founder"
277+ assert engineers [0 ].competencies == "Python, SQL"
278+ assert sysadmins [0 ].competencies == "Linux, Networking"
279+
280+ # Check polymorphic identities
281+ assert managers [0 ].type == "manager"
282+ assert principals [0 ].type == "principal"
283+ assert engineers [0 ].type == "engineer"
284+ assert sysadmins [0 ].type == "sysadmin"
0 commit comments