mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-07 17:31:24 -04:00
146 lines
5.8 KiB
Python
146 lines
5.8 KiB
Python
from sqlalchemy import *
|
|
import sys
|
|
|
|
# this example illustrates how to create a relationship to a list of objects,
|
|
# where each object in the list has a different type. The typed objects will
|
|
# extend from a common base class, although this same approach can be used
|
|
# with
|
|
|
|
db = create_engine('sqlite://', echo=True, echo_uow=False)
|
|
#db = create_engine('postgres://user=scott&password=tiger&host=127.0.0.1&database=test', echo=True, echo_uow=False)
|
|
|
|
# a table to store companies
|
|
companies = Table('companies', db,
|
|
Column('company_id', Integer, primary_key=True),
|
|
Column('name', String(50))).create()
|
|
|
|
# we will define an inheritance relationship between the table "people" and "engineers",
|
|
# and a second inheritance relationship between the table "people" and "managers"
|
|
people = Table('people', db,
|
|
Column('person_id', Integer, primary_key=True),
|
|
Column('company_id', Integer, ForeignKey('companies.company_id')),
|
|
Column('name', String(50))).create()
|
|
|
|
engineers = Table('engineers', db,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('special_description', String(50))).create()
|
|
|
|
managers = Table('managers', db,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('description', String(50))).create()
|
|
|
|
|
|
# create our classes. The Engineer and Manager classes extend from Person.
|
|
class Person(object):
|
|
def __repr__(self):
|
|
return "Ordinary person %s" % self.name
|
|
class Engineer(Person):
|
|
def __repr__(self):
|
|
return "Engineer %s, description %s" % (self.name, self.special_description)
|
|
class Manager(Person):
|
|
def __repr__(self):
|
|
return "Manager %s, description %s" % (self.name, self.description)
|
|
class Company(object):
|
|
def __repr__(self):
|
|
return "Company %s" % self.name
|
|
|
|
# next we assign Person mappers. Since these are the first mappers we are
|
|
# creating for these classes, they automatically become the "primary mappers", which
|
|
# define the dependency relationships between the classes, so we do a straight
|
|
# inheritance setup, i.e. no modifications to how objects are loaded or anything like that.
|
|
assign_mapper(Person, people)
|
|
assign_mapper(Engineer, engineers, inherits=Person.mapper)
|
|
assign_mapper(Manager, managers, inherits=Person.mapper)
|
|
|
|
# next, we define a query that is going to load Managers and Engineers in one shot.
|
|
# we will use a UNION ALL with an extra hardcoded column to indicate the type of object.
|
|
# this can also be done via several LEFT OUTER JOINS but a UNION is more appropriate
|
|
# since they are distinct result sets.
|
|
# The select() statement is also given an alias 'pjoin', since the mapper requires
|
|
# that all Selectables have a name.
|
|
#
|
|
# TECHNIQUE - when you want to load a certain set of objects from a in one query, all the
|
|
# columns in the Selectable must have unique names. Dont worry about mappers at this point,
|
|
# just worry about making a query where if you were to view the results, you could tell everything
|
|
# you need to know from each row how to construct an object instance from it. this is the
|
|
# essence of "resultset-based-mapping", which is the core ideology of SQLAlchemy.
|
|
#
|
|
person_join = select(
|
|
[people, managers.c.description,column("'manager'").label('type')],
|
|
people.c.person_id==managers.c.person_id).union_all(
|
|
select(
|
|
[people, engineers.c.special_description.label('description'), column("'engineer'").label('type')],
|
|
people.c.person_id==engineers.c.person_id)).alias('pjoin')
|
|
|
|
|
|
# lets print out what this Selectable looks like. The mapper is going to take the selectable and
|
|
# Select off of it, with the flag "use_labels" which indicates to prefix column names with the table
|
|
# name. So here is what our mapper will see:
|
|
print "Person selectable:", str(person_join.select(use_labels=True)), "\n"
|
|
|
|
|
|
# MapperExtension object.
|
|
class PersonLoader(MapperExtension):
|
|
def create_instance(self, mapper, row, imap, class_):
|
|
if row['pjoin_type'] =='engineer':
|
|
e = Engineer()
|
|
e.special_description = row['pjoin_description']
|
|
return e
|
|
elif row['pjoin_type'] =='manager':
|
|
return Manager()
|
|
else:
|
|
return Person()
|
|
ext = PersonLoader()
|
|
|
|
# set up the polymorphic mapper, which maps the person_join we set up to
|
|
# the Person class, using an instance of PersonLoader.
|
|
people_mapper = mapper(Person, person_join, extension=ext)
|
|
|
|
# create a mapper for Company. the 'employees' relationship points to
|
|
# our new people_mapper.
|
|
#
|
|
# the dependency relationships which take effect on commit (i.e. the order of
|
|
# inserts/deletes) will be established against the Person class's primary
|
|
# mapper, and when the Engineer and
|
|
# Manager objects are found in the 'employees' list, the primary mappers
|
|
# for those subclasses will register
|
|
# themselves as dependent on the Person mapper's save operations.
|
|
# (translation: it'll work)
|
|
# TODO: get the eager loading to work (the compound select alias doesnt like being aliased itself)
|
|
assign_mapper(Company, companies, properties={
|
|
'employees': relation(people_mapper, lazy=False, private=True)
|
|
})
|
|
|
|
c = Company(name='company1')
|
|
c.employees.append(Manager(name='pointy haired boss', description='manager1'))
|
|
c.employees.append(Engineer(name='dilbert', special_description='engineer1'))
|
|
c.employees.append(Engineer(name='wally', special_description='engineer2'))
|
|
c.employees.append(Manager(name='jsmith', description='manager2'))
|
|
objectstore.commit()
|
|
|
|
objectstore.clear()
|
|
|
|
c = Company.get(1)
|
|
for e in c.employees:
|
|
print e, e._instance_key
|
|
|
|
print "\n"
|
|
|
|
dilbert = Engineer.mapper.get_by(name='dilbert')
|
|
dilbert.special_description = 'hes dibert!'
|
|
objectstore.commit()
|
|
|
|
objectstore.clear()
|
|
c = Company.get(1)
|
|
for e in c.employees:
|
|
print e, e._instance_key
|
|
|
|
objectstore.delete(c)
|
|
objectstore.commit()
|
|
|
|
|
|
managers.drop()
|
|
engineers.drop()
|
|
people.drop()
|
|
companies.drop()
|