summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-05-16 09:51:06 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2017-05-16 09:51:06 -0400
commit5329fc481ed1dcea0e922fd63def3094bda12c91 (patch)
tree827aaac0ff4b7c9897aa3468d8593fe3fed54c84
parentb5697334789a84e4dec2729053ab6a6640855ac8 (diff)
downloadsqlalchemy-5329fc481ed1dcea0e922fd63def3094bda12c91.tar.gz
modernize and repair inheritance examples
remarkably, the examples for concrete and single were still using classical mappings. Ensure all three examples use modern declarative patterns, each illustrate the identical set of query operations. Use back_populates, flat=True for joins, etc. ensure flake8 linting, correct links and add a link back from newly reworked inheritance documentation. Change-Id: I8465a9badbb0eda804f457ccac599f051ee3c27c
-rw-r--r--doc/build/orm/examples.rst2
-rw-r--r--doc/build/orm/inheritance.rst5
-rw-r--r--examples/inheritance/__init__.py4
-rw-r--r--examples/inheritance/concrete.py193
-rw-r--r--examples/inheritance/joined.py111
-rw-r--r--examples/inheritance/single.py199
6 files changed, 341 insertions, 173 deletions
diff --git a/doc/build/orm/examples.rst b/doc/build/orm/examples.rst
index 25d243022..1a0007ca7 100644
--- a/doc/build/orm/examples.rst
+++ b/doc/build/orm/examples.rst
@@ -106,6 +106,8 @@ Vertical Attribute Mapping
.. automodule:: examples.vertical
+.. _examples_inheritance:
+
Inheritance Mapping Recipes
============================
diff --git a/doc/build/orm/inheritance.rst b/doc/build/orm/inheritance.rst
index b57b8e3e7..2d21e9e78 100644
--- a/doc/build/orm/inheritance.rst
+++ b/doc/build/orm/inheritance.rst
@@ -17,6 +17,11 @@ When mappers are configured in an inheritance relationship, SQLAlchemy has the
ability to load elements :term:`polymorphically`, meaning that a single query can
return objects of multiple types.
+.. seealso::
+
+ :ref:`examples_inheritance` - complete exampes of joined, single and
+ concrete inheritance
+
.. _joined_inheritance:
Joined Table Inheritance
diff --git a/examples/inheritance/__init__.py b/examples/inheritance/__init__.py
index eb3e843ca..c97404376 100644
--- a/examples/inheritance/__init__.py
+++ b/examples/inheritance/__init__.py
@@ -1,6 +1,6 @@
"""Working examples of single-table, joined-table, and concrete-table
-inheritance as described in :ref:`datamapping_inheritance`.
+inheritance as described in :ref:`inheritance_toplevel`.
.. autosource::
-""" \ No newline at end of file
+"""
diff --git a/examples/inheritance/concrete.py b/examples/inheritance/concrete.py
index f9bdc81b4..258f41025 100644
--- a/examples/inheritance/concrete.py
+++ b/examples/inheritance/concrete.py
@@ -1,74 +1,161 @@
-"""Concrete (table-per-class) inheritance example."""
+"""Concrete-table (table-per-class) inheritance example."""
-from sqlalchemy import create_engine, MetaData, Table, Column, Integer, \
- String
-from sqlalchemy.orm import mapper, sessionmaker, polymorphic_union
+from sqlalchemy import Column, Integer, String, \
+ ForeignKey, create_engine, inspect, or_
+from sqlalchemy.orm import relationship, Session, with_polymorphic
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.ext.declarative import ConcreteBase
-metadata = MetaData()
-managers_table = Table('managers', metadata,
- Column('employee_id', Integer, primary_key=True),
- Column('name', String(50)),
- Column('manager_data', String(40))
-)
+Base = declarative_base()
-engineers_table = Table('engineers', metadata,
- Column('employee_id', Integer, primary_key=True),
- Column('name', String(50)),
- Column('engineer_info', String(40))
-)
-engine = create_engine('sqlite:///', echo=True)
-metadata.create_all(engine)
+class Company(Base):
+ __tablename__ = 'company'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
+ employees = relationship(
+ "Person",
+ back_populates='company',
+ cascade='all, delete-orphan')
-class Employee(object):
- def __init__(self, name):
- self.name = name
def __repr__(self):
- return self.__class__.__name__ + " " + self.name
+ return "Company %s" % self.name
-class Manager(Employee):
- def __init__(self, name, manager_data):
- self.name = name
- self.manager_data = manager_data
- def __repr__(self):
- return self.__class__.__name__ + " " + \
- self.name + " " + self.manager_data
-class Engineer(Employee):
- def __init__(self, name, engineer_info):
- self.name = name
- self.engineer_info = engineer_info
+class Person(ConcreteBase, Base):
+ __tablename__ = 'person'
+ id = Column(Integer, primary_key=True)
+ company_id = Column(ForeignKey('company.id'))
+ name = Column(String(50))
+
+ company = relationship("Company", back_populates="employees")
+
+ __mapper_args__ = {
+ 'polymorphic_identity': 'person',
+ }
+
def __repr__(self):
- return self.__class__.__name__ + " " + \
- self.name + " " + self.engineer_info
+ return "Ordinary person %s" % self.name
+
+
+class Engineer(Person):
+ __tablename__ = 'engineer'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
+ company_id = Column(ForeignKey('company.id'))
+ status = Column(String(30))
+ engineer_name = Column(String(30))
+ primary_language = Column(String(30))
+ company = relationship("Company", back_populates="employees")
-pjoin = polymorphic_union({
- 'manager':managers_table,
- 'engineer':engineers_table
-}, 'type', 'pjoin')
+ __mapper_args__ = {
+ 'polymorphic_identity': 'engineer',
+ 'concrete': True
+ }
-employee_mapper = mapper(Employee, pjoin, polymorphic_on=pjoin.c.type)
-manager_mapper = mapper(Manager, managers_table,
- inherits=employee_mapper, concrete=True,
- polymorphic_identity='manager')
-engineer_mapper = mapper(Engineer, engineers_table,
- inherits=employee_mapper, concrete=True,
- polymorphic_identity='engineer')
+ def __repr__(self):
+ return (
+ "Engineer %s, status %s, engineer_name %s, "
+ "primary_language %s" %
+ (
+ self.name, self.status,
+ self.engineer_name, self.primary_language)
+ )
+
+
+class Manager(Person):
+ __tablename__ = 'manager'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
+ company_id = Column(ForeignKey('company.id'))
+ status = Column(String(30))
+ manager_name = Column(String(30))
+
+ company = relationship("Company", back_populates="employees")
+
+ __mapper_args__ = {
+ 'polymorphic_identity': 'manager',
+ 'concrete': True
+ }
+
+ def __repr__(self):
+ return "Manager %s, status %s, manager_name %s" % (
+ self.name, self.status, self.manager_name)
+
+
+engine = create_engine('sqlite://', echo=True)
+Base.metadata.create_all(engine)
+
+session = Session(engine)
+
+c = Company(name='company1', employees=[
+ Manager(
+ name='pointy haired boss',
+ status='AAB',
+ manager_name='manager1'),
+ Engineer(
+ name='dilbert',
+ status='BBA',
+ engineer_name='engineer1',
+ primary_language='java'),
+ Person(name='joesmith'),
+ Engineer(
+ name='wally',
+ status='CGG',
+ engineer_name='engineer2',
+ primary_language='python'),
+ Manager(
+ name='jsmith',
+ status='ABA',
+ manager_name='manager2')
+])
+session.add(c)
+
+session.commit()
+c = session.query(Company).get(1)
+for e in c.employees:
+ print(e, inspect(e).key, e.company)
+assert set([e.name for e in c.employees]) == set(
+ ['pointy haired boss', 'dilbert', 'joesmith', 'wally', 'jsmith'])
+print("\n")
-session = sessionmaker(engine)()
+dilbert = session.query(Person).filter_by(name='dilbert').one()
+dilbert2 = session.query(Engineer).filter_by(name='dilbert').one()
+assert dilbert is dilbert2
-m1 = Manager("pointy haired boss", "manager1")
-e1 = Engineer("wally", "engineer1")
-e2 = Engineer("dilbert", "engineer2")
+dilbert.engineer_name = 'hes dilbert!'
-session.add(m1)
-session.add(e1)
-session.add(e2)
session.commit()
-print(session.query(Employee).all())
+c = session.query(Company).get(1)
+for e in c.employees:
+ print(e)
+
+# query using with_polymorphic.
+eng_manager = with_polymorphic(Person, [Engineer, Manager])
+print(
+ session.query(eng_manager).
+ filter(
+ or_(
+ eng_manager.Engineer.engineer_name == 'engineer1',
+ eng_manager.Manager.manager_name == 'manager2'
+ )
+ ).all()
+)
+
+# illustrate join from Company
+eng_manager = with_polymorphic(Person, [Engineer, Manager])
+print(
+ session.query(Company).
+ join(
+ Company.employees.of_type(eng_manager)
+ ).filter(
+ or_(eng_manager.Engineer.engineer_name == 'engineer1',
+ eng_manager.Manager.manager_name == 'manager2')
+ ).all())
+session.commit()
diff --git a/examples/inheritance/joined.py b/examples/inheritance/joined.py
index 6e0205e04..f9322158e 100644
--- a/examples/inheritance/joined.py
+++ b/examples/inheritance/joined.py
@@ -1,66 +1,79 @@
"""Joined-table (table-per-subclass) inheritance example."""
-from sqlalchemy import Table, Column, Integer, String, \
+from sqlalchemy import Column, Integer, String, \
ForeignKey, create_engine, inspect, or_
from sqlalchemy.orm import relationship, Session, with_polymorphic
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
+
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
- employees = relationship("Person",
- backref='company',
- cascade='all, delete-orphan')
+ employees = relationship(
+ "Person",
+ back_populates='company',
+ cascade='all, delete-orphan')
def __repr__(self):
return "Company %s" % self.name
+
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
- company_id = Column(Integer, ForeignKey('company.id'))
+ company_id = Column(ForeignKey('company.id'))
name = Column(String(50))
type = Column(String(50))
+ company = relationship("Company", back_populates="employees")
+
__mapper_args__ = {
- 'polymorphic_identity':'person',
- 'polymorphic_on':type
+ 'polymorphic_identity': 'person',
+ 'polymorphic_on': type
}
+
def __repr__(self):
return "Ordinary person %s" % self.name
+
class Engineer(Person):
__tablename__ = 'engineer'
- id = Column(Integer, ForeignKey('person.id'), primary_key=True)
+ id = Column(ForeignKey('person.id'), primary_key=True)
status = Column(String(30))
engineer_name = Column(String(30))
primary_language = Column(String(30))
__mapper_args__ = {
- 'polymorphic_identity':'engineer',
+ 'polymorphic_identity': 'engineer',
}
+
def __repr__(self):
- return "Engineer %s, status %s, engineer_name %s, "\
- "primary_language %s" % \
- (self.name, self.status,
- self.engineer_name, self.primary_language)
+ return (
+ "Engineer %s, status %s, engineer_name %s, "
+ "primary_language %s" %
+ (
+ self.name, self.status,
+ self.engineer_name, self.primary_language)
+ )
+
class Manager(Person):
__tablename__ = 'manager'
- id = Column(Integer, ForeignKey('person.id'), primary_key=True)
+ id = Column(ForeignKey('person.id'), primary_key=True)
status = Column(String(30))
manager_name = Column(String(30))
__mapper_args__ = {
- 'polymorphic_identity':'manager',
+ 'polymorphic_identity': 'manager',
}
+
def __repr__(self):
- return "Manager %s, status %s, manager_name %s" % \
- (self.name, self.status, self.manager_name)
+ return "Manager %s, status %s, manager_name %s" % (
+ self.name, self.status, self.manager_name)
engine = create_engine('sqlite://', echo=True)
@@ -73,18 +86,21 @@ c = Company(name='company1', employees=[
name='pointy haired boss',
status='AAB',
manager_name='manager1'),
- Engineer(name='dilbert',
+ Engineer(
+ name='dilbert',
status='BBA',
engineer_name='engineer1',
primary_language='java'),
Person(name='joesmith'),
- Engineer(name='wally',
- status='CGG',
- engineer_name='engineer2',
- primary_language='python'),
- Manager(name='jsmith',
- status='ABA',
- manager_name='manager2')
+ Engineer(
+ name='wally',
+ status='CGG',
+ engineer_name='engineer2',
+ primary_language='python'),
+ Manager(
+ name='jsmith',
+ status='ABA',
+ manager_name='manager2')
])
session.add(c)
@@ -93,8 +109,8 @@ session.commit()
c = session.query(Company).get(1)
for e in c.employees:
print(e, inspect(e).key, e.company)
-assert set([e.name for e in c.employees]) == set(['pointy haired boss',
- 'dilbert', 'joesmith', 'wally', 'jsmith'])
+assert set([e.name for e in c.employees]) == set(
+ ['pointy haired boss', 'dilbert', 'joesmith', 'wally', 'jsmith'])
print("\n")
dilbert = session.query(Person).filter_by(name='dilbert').one()
@@ -110,27 +126,30 @@ for e in c.employees:
print(e)
# query using with_polymorphic.
-eng_manager = with_polymorphic(Person, [Engineer, Manager], aliased=True)
-print(session.query(eng_manager).\
- filter(
- or_(eng_manager.Engineer.engineer_name=='engineer1',
- eng_manager.Manager.manager_name=='manager2'
- )
- ).all())
-
-# illustrate join from Company,
-# We use aliased=True
-# to help when the selectable is used as the target of a join.
-eng_manager = with_polymorphic(Person, [Engineer, Manager], aliased=True)
-print(session.query(Company).\
+eng_manager = with_polymorphic(Person, [Engineer, Manager])
+print(
+ session.query(eng_manager).
+ filter(
+ or_(
+ eng_manager.Engineer.engineer_name == 'engineer1',
+ eng_manager.Manager.manager_name == 'manager2'
+ )
+ ).all()
+)
+
+# illustrate join from Company.
+# flat=True means the tables inside the "polymorphic join" will be aliased.
+# not strictly necessary in this example but helpful for the more general
+# case of joins involving inheritance hierarchies as well as joined eager
+# loading.
+eng_manager = with_polymorphic(Person, [Engineer, Manager], flat=True)
+print(
+ session.query(Company).
join(
- eng_manager,
- Company.employees
+ Company.employees.of_type(eng_manager)
).filter(
- or_(eng_manager.Engineer.engineer_name=='engineer1',
- eng_manager.Manager.manager_name=='manager2')
+ or_(eng_manager.Engineer.engineer_name == 'engineer1',
+ eng_manager.Manager.manager_name == 'manager2')
).all())
session.commit()
-
-
diff --git a/examples/inheritance/single.py b/examples/inheritance/single.py
index 22a6fe027..56397f540 100644
--- a/examples/inheritance/single.py
+++ b/examples/inheritance/single.py
@@ -1,102 +1,157 @@
-"""Single-table inheritance example."""
-
-from sqlalchemy import MetaData, Table, Column, Integer, String, \
- ForeignKey, create_engine
-from sqlalchemy.orm import mapper, relationship, sessionmaker
-
-metadata = MetaData()
-
-# a table to store companies
-companies = Table('companies', metadata,
- Column('company_id', Integer, primary_key=True),
- Column('name', String(50)))
-
-employees_table = Table('employees', metadata,
- Column('employee_id', Integer, primary_key=True),
- Column('company_id', Integer, ForeignKey('companies.company_id')),
- Column('name', String(50)),
- Column('type', String(20)),
- Column('status', String(20)),
- Column('engineer_name', String(50)),
- Column('primary_language', String(50)),
- Column('manager_name', String(50))
-)
+"""Single-table (table-per-hierarchy) inheritance example."""
+
+from sqlalchemy import Column, Integer, String, \
+ ForeignKey, create_engine, inspect, or_
+from sqlalchemy.orm import relationship, Session, with_polymorphic
+from sqlalchemy.ext.declarative import declarative_base, declared_attr
+
+Base = declarative_base()
+
+class Company(Base):
+ __tablename__ = 'company'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
+
+ employees = relationship(
+ "Person",
+ back_populates='company',
+ cascade='all, delete-orphan')
+
+ def __repr__(self):
+ return "Company %s" % self.name
+
+
+class Person(Base):
+ __tablename__ = 'person'
+ id = Column(Integer, primary_key=True)
+ company_id = Column(ForeignKey('company.id'))
+ name = Column(String(50))
+ type = Column(String(50))
+
+ company = relationship("Company", back_populates="employees")
+
+ __mapper_args__ = {
+ 'polymorphic_identity': 'person',
+ 'polymorphic_on': type
+ }
-class Person(object):
- def __init__(self, **kwargs):
- for key, value in kwargs.items():
- setattr(self, key, value)
def __repr__(self):
return "Ordinary person %s" % self.name
+
+
class Engineer(Person):
- def __repr__(self):
- return "Engineer %s, status %s, engineer_name %s, "\
- "primary_language %s" % \
- (self.name, self.status,
- self.engineer_name, self.primary_language)
-class Manager(Person):
- def __repr__(self):
- return "Manager %s, status %s, manager_name %s" % \
- (self.name, self.status, self.manager_name)
-class Company(object):
- def __init__(self, **kwargs):
- for key, value in kwargs.items():
- setattr(self, key, value)
- def __repr__(self):
- return "Company %s" % self.name
-person_mapper = mapper(Person, employees_table,
- polymorphic_on=employees_table.c.type,
- polymorphic_identity='person')
-manager_mapper = mapper(Manager, inherits=person_mapper,
- polymorphic_identity='manager')
-engineer_mapper = mapper(Engineer, inherits=person_mapper,
- polymorphic_identity='engineer')
+ engineer_name = Column(String(30))
+ primary_language = Column(String(30))
-mapper(Company, companies, properties={
- 'employees': relationship(Person, lazy=True, backref='company')
-})
+ # illustrate a single-inh "conflicting" column declaration;
+ # see http://docs.sqlalchemy.org/en/latest/orm/extensions/
+ # declarative/inheritance.html#resolving-column-conflicts
+ @declared_attr
+ def status(cls):
+ return Person.__table__.c.get('status', Column(String(30)))
+ __mapper_args__ = {
+ 'polymorphic_identity': 'engineer',
+ }
-engine = create_engine('sqlite:///', echo=True)
+ def __repr__(self):
+ return (
+ "Engineer %s, status %s, engineer_name %s, "
+ "primary_language %s" %
+ (
+ self.name, self.status,
+ self.engineer_name, self.primary_language)
+ )
+
+
+class Manager(Person):
+ manager_name = Column(String(30))
-metadata.create_all(engine)
+ @declared_attr
+ def status(cls):
+ return Person.__table__.c.get('status', Column(String(30)))
-session = sessionmaker(engine)()
+ __mapper_args__ = {
+ 'polymorphic_identity': 'manager',
+ }
-c = Company(name='company1')
-c.employees.append(Manager(name='pointy haired boss', status='AAB',
- manager_name='manager1'))
-c.employees.append(Engineer(name='dilbert', status='BBA',
- engineer_name='engineer1', primary_language='java'))
-c.employees.append(Person(name='joesmith', status='HHH'))
-c.employees.append(Engineer(name='wally', status='CGG',
- engineer_name='engineer2', primary_language='python'
- ))
-c.employees.append(Manager(name='jsmith', status='ABA',
- manager_name='manager2'))
+ def __repr__(self):
+ return "Manager %s, status %s, manager_name %s" % (
+ self.name, self.status, self.manager_name)
+
+
+engine = create_engine('sqlite://', echo=True)
+Base.metadata.create_all(engine)
+
+session = Session(engine)
+
+c = Company(name='company1', employees=[
+ Manager(
+ name='pointy haired boss',
+ status='AAB',
+ manager_name='manager1'),
+ Engineer(
+ name='dilbert',
+ status='BBA',
+ engineer_name='engineer1',
+ primary_language='java'),
+ Person(name='joesmith'),
+ Engineer(
+ name='wally',
+ status='CGG',
+ engineer_name='engineer2',
+ primary_language='python'),
+ Manager(
+ name='jsmith',
+ status='ABA',
+ manager_name='manager2')
+])
session.add(c)
+
session.commit()
c = session.query(Company).get(1)
for e in c.employees:
- print(e, e.company)
-
+ print(e, inspect(e).key, e.company)
+assert set([e.name for e in c.employees]) == set(
+ ['pointy haired boss', 'dilbert', 'joesmith', 'wally', 'jsmith'])
print("\n")
dilbert = session.query(Person).filter_by(name='dilbert').one()
dilbert2 = session.query(Engineer).filter_by(name='dilbert').one()
assert dilbert is dilbert2
-dilbert.engineer_name = 'hes dibert!'
+dilbert.engineer_name = 'hes dilbert!'
-session.flush()
-session.expunge_all()
+session.commit()
c = session.query(Company).get(1)
for e in c.employees:
print(e)
-session.delete(c)
+# query using with_polymorphic.
+eng_manager = with_polymorphic(Person, [Engineer, Manager])
+print(
+ session.query(eng_manager).
+ filter(
+ or_(
+ eng_manager.Engineer.engineer_name == 'engineer1',
+ eng_manager.Manager.manager_name == 'manager2'
+ )
+ ).all()
+)
+
+# illustrate join from Company,
+eng_manager = with_polymorphic(Person, [Engineer, Manager])
+print(
+ session.query(Company).
+ join(
+ Company.employees.of_type(eng_manager)
+ ).filter(
+ or_(eng_manager.Engineer.engineer_name == 'engineer1',
+ eng_manager.Manager.manager_name == 'manager2')
+ ).all())
+
session.commit()