diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-05-16 09:51:06 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-05-16 09:51:06 -0400 |
commit | 5329fc481ed1dcea0e922fd63def3094bda12c91 (patch) | |
tree | 827aaac0ff4b7c9897aa3468d8593fe3fed54c84 /examples/inheritance | |
parent | b5697334789a84e4dec2729053ab6a6640855ac8 (diff) | |
download | sqlalchemy-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
Diffstat (limited to 'examples/inheritance')
-rw-r--r-- | examples/inheritance/__init__.py | 4 | ||||
-rw-r--r-- | examples/inheritance/concrete.py | 193 | ||||
-rw-r--r-- | examples/inheritance/joined.py | 111 | ||||
-rw-r--r-- | examples/inheritance/single.py | 199 |
4 files changed, 334 insertions, 173 deletions
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() |