Loading Inheritance Hierarchies

When classes are mapped in inheritance hierarchies using the “joined”,“single”, or “concrete” table inheritance styles as described atMapping Class Inheritance Hierarchies, the usual behavior is that a query for aparticular base class will also yield objects corresponding to subclassesas well. When a single query is capable of returning a result witha different class or subclasses per result row, we use the term“polymorphic loading”.

Within the realm of polymorphic loading, specifically with joined and singletable inheritance, there is an additional problem of which subclass attributesare to be queried up front, and which are to be loaded later. When an attributeof a particular subclass is queried up front, we can use it in our query assomething to filter on, and it also will be loaded when we get our objectsback. If it’s not queried up front, it gets loaded later when we first needto access it. Basic control of this behavior is provided using theorm.with_polymorphic() function, as well as two variants, the mapperconfiguration mapper.with_polymorphic in conjunction withthe mapper.polymorphic_load option, and the Query-level Query.with_polymorphic() method. The “with_polymorphic” familyeach provide a means of specifying which specific subclasses of a particularbase class should be included within a query, which implies what columns andtables will be available in the SELECT.

Using with_polymorphic

For the following sections, assume the Employee / Engineer / Managerexamples introduced in Mapping Class Inheritance Hierarchies.

Normally, when a Query specifies the base class of aninheritance hierarchy, only the columns that are local to that baseclass are queried:

  1. session.query(Employee).all()

Above, for both single and joined table inheritance, only the columnslocal to Employee will be present in the SELECT. We may get backinstances of Engineer or Manager, however they will not have theadditional attributes loaded until we first access them, at which point alazy load is emitted.

Similarly, if we wanted to refer to columns mappedto Engineer or Manager in our query that’s against Employee,these columns aren’t available directly in either the single or joined tableinheritance case, since the Employee entity does not refer to these columns(note that for single-table inheritance, this is common if Declarative is used,but not for a classical mapping).

To solve both of these issues, the orm.with_polymorphic() functionprovides a special AliasedClass that represents a range ofcolumns across subclasses. This object can be used in a Querylike any other alias. When queried, it represents all the columns present inthe classes given:

  1. from sqlalchemy.orm import with_polymorphic
  2.  
  3. eng_plus_manager = with_polymorphic(Employee, [Engineer, Manager])
  4.  
  5. query = session.query(eng_plus_manager)

If the above mapping were using joined table inheritance, the SELECTstatement for the above would be:

  1. query.all()
  2. SELECT employee.id AS employee_id,
  3. engineer.id AS engineer_id,
  4. manager.id AS manager_id,
  5. employee.name AS employee_name,
  6. employee.type AS employee_type,
  7. engineer.engineer_info AS engineer_engineer_info,
  8. manager.manager_data AS manager_manager_data
  9. FROM employee
  10. LEFT OUTER JOIN engineer
  11. ON employee.id = engineer.id
  12. LEFT OUTER JOIN manager
  13. ON employee.id = manager.id
  14. []

Where above, the additional tables / columns for “engineer” and “manager” areincluded. Similar behavior occurs in the case of single table inheritance.

orm.with_polymorphic() accepts a single class ormapper, a list of classes/mappers, or the string '*' to indicate allsubclasses:

  1. # include columns for Engineer
  2. entity = with_polymorphic(Employee, Engineer)
  3.  
  4. # include columns for Engineer, Manager
  5. entity = with_polymorphic(Employee, [Engineer, Manager])
  6.  
  7. # include columns for all mapped subclasses
  8. entity = with_polymorphic(Employee, '*')

Using aliasing with with_polymorphic

The orm.with_polymorphic() function also provides “aliasing” of thepolymorphic selectable itself, meaning, two different orm.with_polymorphic()entities, referring to the same class hierarchy, can be used together. Thisis available using the orm.with_polymorphic.aliased flag.For a polymorphic selectable that is across multiple tables, the default behavioris to wrap the selectable into a subquery. Below we emit a query that willselect for “employee or manager” paired with “employee or engineer” on employeeswith the same name:

  1. engineer_employee = with_polymorphic(
  2. Employee, [Engineer], aliased=True)
  3. manager_employee = with_polymorphic(
  4. Employee, [Manager], aliased=True)
  5.  
  6. q = s.query(engineer_employee, manager_employee).\
  7. join(
  8. manager_employee,
  9. and_(
  10. engineer_employee.id > manager_employee.id,
  11. engineer_employee.name == manager_employee.name
  12. )
  13. )
  14. q.all()
  15. SELECT
  16. anon_1.employee_id AS anon_1_employee_id,
  17. anon_1.employee_name AS anon_1_employee_name,
  18. anon_1.employee_type AS anon_1_employee_type,
  19. anon_1.engineer_id AS anon_1_engineer_id,
  20. anon_1.engineer_engineer_name AS anon_1_engineer_engineer_name,
  21. anon_2.employee_id AS anon_2_employee_id,
  22. anon_2.employee_name AS anon_2_employee_name,
  23. anon_2.employee_type AS anon_2_employee_type,
  24. anon_2.manager_id AS anon_2_manager_id,
  25. anon_2.manager_manager_name AS anon_2_manager_manager_name
  26. FROM (
  27. SELECT
  28. employee.id AS employee_id,
  29. employee.name AS employee_name,
  30. employee.type AS employee_type,
  31. engineer.id AS engineer_id,
  32. engineer.engineer_name AS engineer_engineer_name
  33. FROM employee
  34. LEFT OUTER JOIN engineer ON employee.id = engineer.id
  35. ) AS anon_1
  36. JOIN (
  37. SELECT
  38. employee.id AS employee_id,
  39. employee.name AS employee_name,
  40. employee.type AS employee_type,
  41. manager.id AS manager_id,
  42. manager.manager_name AS manager_manager_name
  43. FROM employee
  44. LEFT OUTER JOIN manager ON employee.id = manager.id
  45. ) AS anon_2
  46. ON anon_1.employee_id > anon_2.employee_id
  47. AND anon_1.employee_name = anon_2.employee_name

The creation of subqueries above is very verbose. While it creates the bestencapsulation of the two distinct queries, it may be inefficient.orm.with_polymorphic() includes an additional flag to help with thissituation, orm.with_polymorphic.flat, which will “flatten” thesubquery / join combination into straight joins, applying aliasing to theindividual tables instead. Setting orm.with_polymorphic.flatimplies orm.with_polymorphic.aliased, so only one flagis necessary:

  1. engineer_employee = with_polymorphic(
  2. Employee, [Engineer], flat=True)
  3. manager_employee = with_polymorphic(
  4. Employee, [Manager], flat=True)
  5.  
  6. q = s.query(engineer_employee, manager_employee).\
  7. join(
  8. manager_employee,
  9. and_(
  10. engineer_employee.id > manager_employee.id,
  11. engineer_employee.name == manager_employee.name
  12. )
  13. )
  14. q.all()
  15. SELECT
  16. employee_1.id AS employee_1_id,
  17. employee_1.name AS employee_1_name,
  18. employee_1.type AS employee_1_type,
  19. engineer_1.id AS engineer_1_id,
  20. engineer_1.engineer_name AS engineer_1_engineer_name,
  21. employee_2.id AS employee_2_id,
  22. employee_2.name AS employee_2_name,
  23. employee_2.type AS employee_2_type,
  24. manager_1.id AS manager_1_id,
  25. manager_1.manager_name AS manager_1_manager_name
  26. FROM employee AS employee_1
  27. LEFT OUTER JOIN engineer AS engineer_1
  28. ON employee_1.id = engineer_1.id
  29. JOIN (
  30. employee AS employee_2
  31. LEFT OUTER JOIN manager AS manager_1
  32. ON employee_2.id = manager_1.id
  33. )
  34. ON employee_1.id > employee_2.id
  35. AND employee_1.name = employee_2.name

Note above, when using orm.with_polymorphic.flat, it is often thecase when used in conjunction with joined table inheritance that we get aright-nested JOIN in our statement. Some older databases, in particular olderversions of SQLite, may have a problem with this syntax, although virtually allmodern database versions now support this syntax.

Note

The orm.with_polymorphic.flat flag only applies to the useof with_polymorphic with joined table inheritance and whenthe with_polymorphic.selectable argument is not used.

Referring to Specific Subclass Attributes

The entity returned by orm.with_polymorphic() is an AliasedClassobject, which can be used in a Query like any other alias, includingnamed attributes for those attributes on the Employee class. In ourprevious example, eng_plus_manager becomes the entity that we use to refer to thethree-way outer join above. It also includes namespaces for each class namedin the list of classes, so that attributes specific to those subclasses can becalled upon as well. The following example illustrates calling upon attributesspecific to Engineer as well as Manager in terms of eng_plus_manager:

  1. eng_plus_manager = with_polymorphic(Employee, [Engineer, Manager])
  2. query = session.query(eng_plus_manager).filter(
  3. or_(
  4. eng_plus_manager.Engineer.engineer_info=='x',
  5. eng_plus_manager.Manager.manager_data=='y'
  6. )
  7. )

Setting with_polymorphic at mapper configuration time

The orm.with_polymorphic() function serves the purpose of allowing“eager” loading of attributes from subclass tables, as well as the abilityto refer to the attributes from subclass tables at query time. Historically,the “eager loading” of columns has been the more important part of theequation. So just as eager loading for relationships can be specifiedas a configurational option, the mapper.with_polymorphicconfiguration parameter allows an entity to use a polymorphic load bydefault. We can add the parameter to our Employee mappingfirst introduced at Joined Table Inheritance:

  1. class Employee(Base):
  2. __tablename__ = 'employee'
  3. id = Column(Integer, primary_key=True)
  4. name = Column(String(50))
  5. type = Column(String(50))
  6.  
  7. __mapper_args__ = {
  8. 'polymorphic_identity':'employee',
  9. 'polymorphic_on':type,
  10. 'with_polymorphic': '*'
  11. }

Above is a common setting for mapper.with_polymorphic,which is to indicate an asterisk to load all subclass columns. In thecase of joined table inheritance, this optionshould be used sparingly, as it implies that the mapping will always emita (often large) series of LEFT OUTER JOIN to many tables, which is notefficient from a SQL perspective. For single table inheritance, specifying theasterisk is often a good idea as the load is still against a single table only,but an additional lazy load of subclass-mapped columns will be prevented.

Using orm.with_polymorphic() or Query.with_polymorphic()will override the mapper-level mapper.with_polymorphic setting.

The mapper.with_polymorphic option also accepts a list ofclasses just like orm.with_polymorphic() to polymorphically load amonga subset of classes. However, when using Declarative, providing classesto this list is not directly possible as the subclasses we’d like to addare not available yet. Instead, we can specify on each subclassthat they should individually participate in polymorphic loading bydefault using the mapper.polymorphic_load parameter:

  1. class Engineer(Employee):
  2. __tablename__ = 'engineer'
  3. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  4. engineer_info = Column(String(50))
  5. __mapper_args__ = {
  6. 'polymorphic_identity':'engineer',
  7. 'polymorphic_load': 'inline'
  8. }
  9.  
  10. class Manager(Employee):
  11. __tablename__ = 'manager'
  12. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  13. manager_data = Column(String(50))
  14. __mapper_args__ = {
  15. 'polymorphic_identity':'manager',
  16. 'polymorphic_load': 'inline'
  17. }

Setting the mapper.polymorphic_load parameter to the value"inline" means that the Engineer and Manager classes aboveare part of the “polymorphic load” of the base Employee class by default,exactly as though they had been appended to themapper.with_polymorphic list of classes.

Setting with_polymorphic against a query

The orm.with_polymorphic() function evolved from a query-levelmethod Query.with_polymorphic(). This method has the same purposeas orm.with_polymorphic(), except is not asflexible in its usage patterns in that it only applies to the first entityof the Query. It then takes effect for all occurrences ofthat entity, so that the entity (and its subclasses) can be referred todirectly, rather than using an alias object. For simple cases it might beconsidered to be more succinct:

  1. session.query(Employee).\
  2. with_polymorphic([Engineer, Manager]).\
  3. filter(
  4. or_(
  5. Engineer.engineer_info=='w',
  6. Manager.manager_data=='q'
  7. )
  8. )

The Query.with_polymorphic() method has a more complicated jobthan the orm.with_polymorphic() function, as it needs to correctlytransform entities like Engineer and Manager appropriately, butnot interfere with other entities. If its flexibility is lacking, switchto using orm.with_polymorphic().

Polymorphic Selectin Loading

An alternative to using the orm.with_polymorphic() family offunctions to “eagerly” load the additional subclasses on an inheritancemapping, primarily when using joined table inheritance, is to use polymorphic“selectin” loading. This is an eager loadingfeature which works similarly to the Select IN loading featureof relationship loading. Given our example mapping, we can instructa load of Employee to emit an extra SELECT per subclass by usingthe orm.selectin_polymorphic() loader option:

  1. from sqlalchemy.orm import selectin_polymorphic
  2.  
  3. query = session.query(Employee).options(
  4. selectin_polymorphic(Employee, [Manager, Engineer])
  5. )

When the above query is run, two additional SELECT statements willbe emitted:

  1. query.all()
  2. SELECT
  3. employee.id AS employee_id,
  4. employee.name AS employee_name,
  5. employee.type AS employee_type
  6. FROM employee
  7. ()
  8. SELECT
  9. engineer.id AS engineer_id,
  10. employee.id AS employee_id,
  11. employee.type AS employee_type,
  12. engineer.engineer_name AS engineer_engineer_name
  13. FROM employee JOIN engineer ON employee.id = engineer.id
  14. WHERE employee.id IN (?, ?) ORDER BY employee.id
  15. (1, 2)
  16. SELECT
  17. manager.id AS manager_id,
  18. employee.id AS employee_id,
  19. employee.type AS employee_type,
  20. manager.manager_name AS manager_manager_name
  21. FROM employee JOIN manager ON employee.id = manager.id
  22. WHERE employee.id IN (?) ORDER BY employee.id
  23. (3,)

We can similarly establish the above style of loading to take placeby default by specifying the mapper.polymorphic_load parameter,using the value "selectin" on a per-subclass basis:

  1. class Employee(Base):
  2. __tablename__ = 'employee'
  3. id = Column(Integer, primary_key=True)
  4. name = Column(String(50))
  5. type = Column(String(50))
  6.  
  7. __mapper_args__ = {
  8. 'polymorphic_identity': 'employee',
  9. 'polymorphic_on': type
  10. }
  11.  
  12. class Engineer(Employee):
  13. __tablename__ = 'engineer'
  14. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  15. engineer_name = Column(String(30))
  16.  
  17. __mapper_args__ = {
  18. 'polymorphic_load': 'selectin',
  19. 'polymorphic_identity': 'engineer',
  20. }
  21.  
  22. class Manager(Employee):
  23. __tablename__ = 'manager'
  24. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  25. manager_name = Column(String(30))
  26.  
  27. __mapper_args__ = {
  28. 'polymorphic_load': 'selectin',
  29. 'polymorphic_identity': 'manager',
  30. }

Unlike when using orm.with_polymorphic(), when using theorm.selectin_polymorphic() style of loading, we do not have theability to refer to the Engineer or Manager entities within our mainquery as filter, order by, or other criteria, as these entities are not presentin the initial query that is used to locate results. However, we can applyloader options that apply towards Engineer or Manager, which will takeeffect when the secondary SELECT is emitted. Below we assume Manager hasan additional relationship Manager.paperwork, that we’d like to eagerlyload as well. We can use any type of eager loading, such as joined eagerloading via the joinedload() function:

  1. from sqlalchemy.orm import joinedload
  2. from sqlalchemy.orm import selectin_polymorphic
  3.  
  4. query = session.query(Employee).options(
  5. selectin_polymorphic(Employee, [Manager, Engineer]),
  6. joinedload(Manager.paperwork)
  7. )

Using the query above, we get three SELECT statements emitted, howeverthe one against Manager will be:

  1. SELECT
  2. manager.id AS manager_id,
  3. employee.id AS employee_id,
  4. employee.type AS employee_type,
  5. manager.manager_name AS manager_manager_name,
  6. paperwork_1.id AS paperwork_1_id,
  7. paperwork_1.manager_id AS paperwork_1_manager_id,
  8. paperwork_1.data AS paperwork_1_data
  9. FROM employee JOIN manager ON employee.id = manager.id
  10. LEFT OUTER JOIN paperwork AS paperwork_1
  11. ON manager.id = paperwork_1.manager_id
  12. WHERE employee.id IN (?) ORDER BY employee.id
  13. (3,)

Note that selectin polymorphic loading has similar caveats as that ofselectin relationship loading; for entities that make use of a compositeprimary key, the database in use must support tuples with “IN”, currentlyknown to work with MySQL and PostgreSQL.

New in version 1.2.

Warning

The selectin polymorphic loading feature should be consideredas experimental within early releases of the 1.2 series.

Combining selectin and with_polymorphic

Note

works as of 1.2.0b3

With careful planning, selectin loading can be applied against a hierarchythat itself uses “with_polymorphic”. A particular use case is that ofusing selectin loading to load a joined-inheritance subtable, which thenuses “with_polymorphic” to refer to further sub-classes, which may bejoined- or single-table inheritance. If we added a class VicePresident thatextends Manager using single-table inheritance, we could ensure thata load of Manager also fully loads VicePresident subtypes at the same time:

  1. # use "Employee" example from the enclosing section
  2.  
  3. class Manager(Employee):
  4. __tablename__ = 'manager'
  5. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  6. manager_name = Column(String(30))
  7.  
  8. __mapper_args__ = {
  9. 'polymorphic_load': 'selectin',
  10. 'polymorphic_identity': 'manager',
  11. }
  12.  
  13. class VicePresident(Manager):
  14. vp_info = Column(String(30))
  15.  
  16. __mapper_args__ = {
  17. "polymorphic_load": "inline",
  18. "polymorphic_identity": "vp"
  19. }

Above, we add a vp_info column to the manager table, local to theVicePresident subclass. This subclass is linked to the polymorphicidentity "vp" which refers to rows which have this data. By setting theload style to “inline”, it means that a load of Manager objects will alsoensure that the vp_info column is queried for in the same SELECT statement.A query against Employee that encounters a Manager row would emitsimilarly to the following:

  1. SELECT employee.id AS employee_id, employee.name AS employee_name,
  2. employee.type AS employee_type
  3. FROM employee
  4. )
  5.  
  6. SELECT manager.id AS manager_id, employee.id AS employee_id,
  7. employee.type AS employee_type,
  8. manager.manager_name AS manager_manager_name,
  9. manager.vp_info AS manager_vp_info
  10. FROM employee JOIN manager ON employee.id = manager.id
  11. WHERE employee.id IN (?) ORDER BY employee.id
  12. (1,)

Combining “selectin” polymorhic loading with query-timeorm.with_polymorphic() usage is also possible (though this is veryouter-space stuff!); assuming the above mappings had no polymorphic_loadset up, we could get the same result as follows:

  1. from sqlalchemy.orm import with_polymorphic, selectin_polymorphic
  2.  
  3. manager_poly = with_polymorphic(Manager, [VicePresident])
  4.  
  5. s.query(Employee).options(
  6. selectin_polymorphic(Employee, [manager_poly])).all()

Referring to specific subtypes on relationships

Mapped attributes which correspond to a relationship() are usedin querying in order to refer to the linkage between two mappings. Commonuses for this are to refer to a relationship() in Query.join()as well as in loader options like joinedload(). When usingrelationship() where the target class is an inheritance hierarchy,the API allows that the join, eager load, or other linkage should target a specificsubclass, alias, or orm.with_polymorphic() alias, of that class hierarchy,rather than the class directly targeted by the relationship().

The of_type() method allows theconstruction of joins along relationship() paths whilenarrowing the criterion to specific derived aliases or subclasses. Suppose theemployees table represents a collection of employees which are associatedwith a Company object. We’ll add a company_id column to theemployees table and a new table companies:

  1. class Company(Base):
  2. __tablename__ = 'company'
  3. id = Column(Integer, primary_key=True)
  4. name = Column(String(50))
  5. employees = relationship("Employee",
  6. backref='company')
  7.  
  8. class Employee(Base):
  9. __tablename__ = 'employee'
  10. id = Column(Integer, primary_key=True)
  11. type = Column(String(20))
  12. company_id = Column(Integer, ForeignKey('company.id'))
  13. __mapper_args__ = {
  14. 'polymorphic_on':type,
  15. 'polymorphic_identity':'employee',
  16. }
  17.  
  18. class Engineer(Employee):
  19. __tablename__ = 'engineer'
  20. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  21. engineer_info = Column(String(50))
  22. __mapper_args__ = {'polymorphic_identity':'engineer'}
  23.  
  24. class Manager(Employee):
  25. __tablename__ = 'manager'
  26. id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  27. manager_data = Column(String(50))
  28. __mapper_args__ = {'polymorphic_identity':'manager'}

When querying from Company onto the Employee relationship, theQuery.join() method as well as operators like PropComparator.any()and PropComparator.has() will createa join from company to employee, without including engineer ormanager in the mix. If we wish to have criterion which is specificallyagainst the Engineer class, we can tell those methods to join or subqueryagainst the set of columns representing the subclass using theof_type() operator:

  1. session.query(Company).\
  2. join(Company.employees.of_type(Engineer)).\
  3. filter(Engineer.engineer_info=='someinfo')

Similarly, to join from Company to the polymorphic entity that includes bothEngineer and Manager columns:

  1. manager_and_engineer = with_polymorphic(
  2. Employee, [Manager, Engineer])
  3.  
  4. session.query(Company).\
  5. join(Company.employees.of_type(manager_and_engineer)).\
  6. filter(
  7. or_(
  8. manager_and_engineer.Engineer.engineer_info == 'someinfo',
  9. manager_and_engineer.Manager.manager_data == 'somedata'
  10. )
  11. )

The PropComparator.any() and PropComparator.has() operators alsocan be used with of_type(),such as when the embedded criterion is in terms of a subclass:

  1. session.query(Company).\
  2. filter(
  3. Company.employees.of_type(Engineer).
  4. any(Engineer.engineer_info=='someinfo')
  5. ).all()

Eager Loading of Specific or Polymorphic Subtypes

The joinedload(), subqueryload(), contains_eager() andother eagerloader options supportpaths which make use of of_type().Below, we load Company rows while eagerly loading related Engineerobjects, querying the employee and engineer tables simultaneously:

  1. session.query(Company).\
  2. options(
  3. subqueryload(Company.employees.of_type(Engineer)).
  4. subqueryload(Engineer.machines)
  5. )
  6. )

As is the case with Query.join(), of_type()can be used to combine eager loading and orm.with_polymorphic(),so that all sub-attributes of all referenced subtypescan be loaded:

  1. manager_and_engineer = with_polymorphic(
  2. Employee, [Manager, Engineer],
  3. flat=True)
  4.  
  5. session.query(Company).\
  6. options(
  7. joinedload(
  8. Company.employees.of_type(manager_and_engineer)
  9. )
  10. )

Note

When using with_polymorphic() in conjunction withjoinedload(), the with_polymorphic() object must be againstan “aliased” object, that is an instance of Alias, so that thepolymorphic selectable is aliased (an informative error message is raisedotherwise).

The typical way to do this is to include thewith_polymorphic.aliased or flat flag, which willapply this aliasing automatically. However, if thewith_polymorphic.selectable argument is being used to pass anobject that is already an Alias object then this flag shouldnot be set. The “flat” option implies the “aliased” option and is analternate form of aliasing against join objects that produces fewersubqueries.

Once of_type() is the target of the eager load,that’s the entity we would use for subsequent chaining, not the original classor derived class. If we wanted to further eager load a collection on theeager-loaded Engineer class, we access this class from the namespace of theorm.with_polymorphic() object:

  1. session.query(Company).\
  2. options(
  3. joinedload(Company.employees.of_type(manager_and_engineer)).\
  4. subqueryload(manager_and_engineer.Engineer.computers)
  5. )
  6. )

Loading objects with joined table inheritance

When using joined table inheritance, if we query for a specific subclassthat represents a JOIN of two tables such as our Engineer examplefrom the inheritance section, the SQL emitted is a join:

  1. session.query(Engineer).all()

The above query will emit SQL like:

  1. SELECT employee.id AS employee_id,
  2. employee.name AS employee_name, employee.type AS employee_type,
  3. engineer.name AS engineer_name
  4. FROM employee JOIN engineer
  5. ON employee.id = engineer.id

We will then get a collection of Engineer objects back, which willcontain all columns from employee and engineer loaded.

However, when emitting a Query against a base class, the behavioris to load only from the base table:

  1. session.query(Employee).all()

Above, the default behavior would be to SELECT only from the employeetable and not from any “sub” tables (engineer and manager, in ourprevious examples):

  1. SELECT employee.id AS employee_id,
  2. employee.name AS employee_name, employee.type AS employee_type
  3. FROM employee
  4. []

After a collection of Employee objects has been returned from thequery, and as attributes are requested from those Employee objects which arerepresented in either the engineer or manager child tables, a secondload is issued for the columns in that related row, if the data was notalready loaded. So above, after accessing the objects you’d see further SQLissued along the lines of:

  1. SELECT manager.id AS manager_id,
  2. manager.manager_data AS manager_manager_data
  3. FROM manager
  4. WHERE ? = manager.id
  5. [5]
  6. SELECT engineer.id AS engineer_id,
  7. engineer.engineer_info AS engineer_engineer_info
  8. FROM engineer
  9. WHERE ? = engineer.id
  10. [2]

The orm.with_polymorphic()function and related configuration options allow us to instead emit a JOIN upfront which will conditionally load against employee, engineer, ormanager, very much like joined eager loading works for relationships,removing the necessity for a second per-entity load:

  1. from sqlalchemy.orm import with_polymorphic
  2.  
  3. eng_plus_manager = with_polymorphic(Employee, [Engineer, Manager])
  4.  
  5. query = session.query(eng_plus_manager)

The above produces a query which joins the employee table to both theengineer and manager tables like the following:

  1. query.all()
  2. SELECT employee.id AS employee_id,
  3. engineer.id AS engineer_id,
  4. manager.id AS manager_id,
  5. employee.name AS employee_name,
  6. employee.type AS employee_type,
  7. engineer.engineer_info AS engineer_engineer_info,
  8. manager.manager_data AS manager_manager_data
  9. FROM employee
  10. LEFT OUTER JOIN engineer
  11. ON employee.id = engineer.id
  12. LEFT OUTER JOIN manager
  13. ON employee.id = manager.id
  14. []

The section Using with_polymorphic discusses the orm.with_polymorphic()function and its configurational variants.

See also

Using with_polymorphic

Loading objects with single table inheritance

In modern Declarative, single inheritance mappings produce Columnobjects that are mapped only to a subclass, and not available from thesuperclass, even though they are present on the same table.In our example from Single Table Inheritance, the Manager mapping for example had aColumn specified:

  1. class Manager(Employee):
  2. manager_data = Column(String(50))
  3.  
  4. __mapper_args__ = {
  5. 'polymorphic_identity':'manager'
  6. }

Above, there would be no Employee.manager_dataattribute, even though the employee table has a manager_data column.A query against Manager will include this column in the query, as wellas an IN clause to limit rows only to Manager objects:

  1. session.query(Manager).all()
  2. SELECT
  3. employee.id AS employee_id,
  4. employee.name AS employee_name,
  5. employee.type AS employee_type,
  6. employee.manager_data AS employee_manager_data
  7. FROM employee
  8. WHERE employee.type IN (?)
  9. ('manager',)

However, in a similar way to that of joined table inheritance, a queryagainst Employee will only query for columns mapped to Employee:

  1. session.query(Employee).all()
  2. SELECT employee.id AS employee_id,
  3. employee.name AS employee_name,
  4. employee.type AS employee_type
  5. FROM employee

If we get back an instance of Manager from our result, accessingadditional columns only mapped to Manager emits a lazy loadfor those columns, in a similar way to joined inheritance:

  1. SELECT employee.manager_data AS employee_manager_data
  2. FROM employee
  3. WHERE employee.id = ? AND employee.type IN (?)

The orm.with_polymorphic() function serves a similar role as joinedinheritance in the case of single inheritance; it allows both for eager loadingof subclass attributes as well as specification of subclasses in a query,just without the overhead of using OUTER JOIN:

  1. employee_poly = with_polymorphic(Employee, '*')
  2.  
  3. q = session.query(employee_poly).filter(
  4. or_(
  5. employee_poly.name == 'a',
  6. employee_poly.Manager.manager_data == 'b'
  7. )
  8. )

Above, our query remains against a single table however we can refer to thecolumns present in Manager or Engineer using the “polymorphic” namespace.Since we specified "*" for the entities, both Engineer andManager will be loaded at once. SQL emitted would be:

  1. q.all()
  2. SELECT
  3. employee.id AS employee_id, employee.name AS employee_name,
  4. employee.type AS employee_type,
  5. employee.manager_data AS employee_manager_data,
  6. employee.engineer_info AS employee_engineer_info
  7. FROM employee
  8. WHERE employee.name = :name_1
  9. OR employee.manager_data = :manager_data_1

Inheritance Loading API

  • sqlalchemy.orm.withpolymorphic(_base, classes, selectable=False, flat=False, polymorphic_on=None, aliased=False, innerjoin=False, usemapper_path=False, existingalias=None)
  • Produce an AliasedClass construct which specifiescolumns for descendant mappers of the given base.

Using this method will ensure that each descendant mapper’stables are included in the FROM clause, and will allow filter()criterion to be used against those tables. The resultinginstances will also have those columns already loaded so thatno “post fetch” of those columns will be required.

See also

Using with_polymorphic - full discussion oform.with_polymorphic().

  • Parameters
    • base – Base class to be aliased.

    • classes – a single class or mapper, or list ofclass/mappers, which inherit from the base class.Alternatively, it may also be the string '*', in which caseall descending mapped classes will be added to the FROM clause.

    • aliased – when True, the selectable will be wrapped in analias, that is (SELECT * FROM <fromclauses>) AS anon_1.This can be important when using the with_polymorphic()to create the target of a JOIN on a backend that does notsupport parenthesized joins, such as SQLite and olderversions of MySQL. However if thewith_polymorphic.selectable parameter is in usewith an existing Alias construct, then you should notset this flag.

    • flat

Boolean, will be passed through to theFromClause.alias() call so that aliases of Joinobjects don’t include an enclosing SELECT. This can lead to moreefficient queries in many circumstances. A JOIN against a nested JOINwill be rewritten as a JOIN against an aliased SELECT subquery onbackends that don’t support this syntax.

Setting flat to True implies the aliased flag isalso True.

New in version 0.9.0.

See also

Join.alias()

  1. -

selectable – a table or select() statement that willbe used in place of the generated FROM clause. This argument isrequired if any of the desired classes use concrete tableinheritance, since SQLAlchemy currently cannot generate UNIONsamong tables automatically. If used, the selectable argumentmust represent the full set of tables and columns mapped by everymapped class. Otherwise, the unaccounted mapped columns willresult in their table being appended directly to the FROM clausewhich will usually lead to incorrect results.

  1. -

polymorphic_on – a column to be used as the “discriminator”column for the given selectable. If not given, the polymorphic_onattribute of the base classes’ mapper will be used, if any. Thisis useful for mappings that don’t have polymorphic loadingbehavior by default.

  1. -

innerjoin – if True, an INNER JOIN will be used. This shouldonly be specified if querying for one specific subtype only

  • sqlalchemy.orm.selectinpolymorphic(_base_cls, classes)
  • Indicate an eager load should take place for all attributesspecific to a subclass.

This uses an additional SELECT with IN against all matched primarykey values, and is the per-query analogue to the "selectin"setting on the mapper.polymorphic_load parameter.

New in version 1.2.

See also

inheritance_polymorphic_load