diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-05-25 14:20:23 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-05-25 14:20:23 +0000 |
| commit | bb79e2e871d0a4585164c1a6ed626d96d0231975 (patch) | |
| tree | 6d457ba6c36c408b45db24ec3c29e147fe7504ff /examples/polymorph | |
| parent | 4fc3a0648699c2b441251ba4e1d37a9107bd1986 (diff) | |
| download | sqlalchemy-bb79e2e871d0a4585164c1a6ed626d96d0231975.tar.gz | |
merged 0.2 branch into trunk; 0.1 now in sqlalchemy/branches/rel_0_1
Diffstat (limited to 'examples/polymorph')
| -rw-r--r-- | examples/polymorph/concrete.py | 65 | ||||
| -rw-r--r-- | examples/polymorph/polymorph.py | 177 | ||||
| -rw-r--r-- | examples/polymorph/polymorph2.py | 136 | ||||
| -rw-r--r-- | examples/polymorph/single.py | 86 |
4 files changed, 222 insertions, 242 deletions
diff --git a/examples/polymorph/concrete.py b/examples/polymorph/concrete.py new file mode 100644 index 000000000..593d3f480 --- /dev/null +++ b/examples/polymorph/concrete.py @@ -0,0 +1,65 @@ +from sqlalchemy import * + +metadata = MetaData() + +managers_table = Table('managers', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('manager_data', String(40)) +) + +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 Employee(object): + def __init__(self, name): + self.name = name + def __repr__(self): + return self.__class__.__name__ + " " + 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 + def __repr__(self): + return self.__class__.__name__ + " " + self.name + " " + self.engineer_info + + +pjoin = polymorphic_union({ + 'manager':managers_table, + 'engineer':engineers_table +}, 'type', 'pjoin') + +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') + + +session = create_session(bind_to=engine) + +m1 = Manager("pointy haired boss", "manager1") +e1 = Engineer("wally", "engineer1") +e2 = Engineer("dilbert", "engineer2") + +session.save(m1) +session.save(e1) +session.save(e2) +session.flush() + +employees = session.query(Employee).select() +print [e for e in employees] + diff --git a/examples/polymorph/polymorph.py b/examples/polymorph/polymorph.py index d105a64ea..76a03b99d 100644 --- a/examples/polymorph/polymorph.py +++ b/examples/polymorph/polymorph.py @@ -1,145 +1,110 @@ from sqlalchemy import * -import sys +import sys, sets -# 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 +# this example illustrates a polymorphic load of two classes, where each class has a very +# different set of properties -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) +metadata = BoundMetaData('sqlite://', echo='debug') # a table to store companies -companies = Table('companies', db, +companies = Table('companies', metadata, Column('company_id', Integer, primary_key=True), - Column('name', String(50))).create() + Column('name', String(50))) # 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, +people = Table('people', metadata, Column('person_id', Integer, primary_key=True), Column('company_id', Integer, ForeignKey('companies.company_id')), - Column('name', String(50))).create() + Column('name', String(50)), + Column('type', String(30))) -engineers = Table('engineers', db, +engineers = Table('engineers', metadata, Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('special_description', String(50))).create() + Column('status', String(30)), + Column('engineer_name', String(50)), + Column('primary_language', String(50)), + ) -managers = Table('managers', db, +managers = Table('managers', metadata, Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('description', String(50))).create() + Column('status', String(30)), + Column('manager_name', String(50)) + ) + +metadata.create_all() - # create our classes. The Engineer and Manager classes extend from Person. class Person(object): + def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): + setattr(self, key, value) 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) + 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, description %s" % (self.name, self.description) + 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.iteritems(): + setattr(self, key, value) 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() +# create a union that represents both types of joins. +person_join = polymorphic_union( + { + 'engineer':people.join(engineers), + 'manager':people.join(managers), + 'person':people.select(people.c.type=='person'), + }, None, 'pjoin') -objectstore.clear() +#person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_identity='person') +person_mapper = mapper(Person, people, select_table=person_join,polymorphic_on=person_join.c.type, polymorphic_identity='person') +mapper(Engineer, engineers, inherits=person_mapper, polymorphic_identity='engineer') +mapper(Manager, managers, inherits=person_mapper, polymorphic_identity='manager') -c = Company.get(1) -for e in c.employees: - print e, e._instance_key +mapper(Company, companies, properties={ + 'employees': relation(Person, lazy=False, private=True, backref='company') +}) +session = create_session(echo_uow=False) +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')) +session.save(c) +print session.new +session.flush() +#sys.exit() +session.clear() + +c = session.query(Company).get(1) +for e in c.employees: + print e, e._instance_key, e.company +assert sets.Set([e.name for e in c.employees]) == sets.Set(['pointy haired boss', 'dilbert', 'joesmith', 'wally', 'jsmith']) print "\n" -dilbert = Engineer.mapper.get_by(name='dilbert') -dilbert.special_description = 'hes dibert!' -objectstore.commit() +dilbert = session.query(Person).get_by(name='dilbert') +dilbert2 = session.query(Engineer).get_by(name='dilbert') +assert dilbert is dilbert2 + +dilbert.engineer_name = 'hes dibert!' -objectstore.clear() -c = Company.get(1) +session.flush() +session.clear() + +c = session.query(Company).get(1) for e in c.employees: print e, e._instance_key -objectstore.delete(c) -objectstore.commit() - +session.delete(c) +session.flush() -managers.drop() -engineers.drop() -people.drop() -companies.drop() +metadata.drop_all() diff --git a/examples/polymorph/polymorph2.py b/examples/polymorph/polymorph2.py deleted file mode 100644 index 351a06eca..000000000 --- a/examples/polymorph/polymorph2.py +++ /dev/null @@ -1,136 +0,0 @@ -from sqlalchemy import * -import sys - -# this example illustrates a polymorphic load of two classes, where each class has a very -# different set of properties - -db = create_engine('sqlite://', 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('status', String(30)), - Column('engineer_name', String(50)), - Column('primary_language', String(50)), - ).create() - -managers = Table('managers', db, - Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('status', String(30)), - Column('manager_name', 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, 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 __repr__(self): - return "Company %s" % self.name - -# assign plain vanilla mappers -assign_mapper(Person, people) -assign_mapper(Engineer, engineers, inherits=Person.mapper) -assign_mapper(Manager, managers, inherits=Person.mapper) - -# create a union that represents both types of joins. we have to use -# nulls to pad out the disparate columns. -person_join = select( - [ - people, - managers.c.status, - managers.c.manager_name, - null().label('engineer_name'), - null().label('primary_language'), - column("'manager'").label('type') - ], - people.c.person_id==managers.c.person_id).union_all( - select( - [ - people, - engineers.c.status, - null().label('').label('manager_name'), - engineers.c.engineer_name, - engineers.c.primary_language, - column("'engineer'").label('type') - ], - people.c.person_id==engineers.c.person_id)).alias('pjoin') - -print [c for c in person_join.c] - -# MapperExtension object. -class PersonLoader(MapperExtension): - def create_instance(self, mapper, row, imap, class_): - if row[person_join.c.type] =='engineer': - return Engineer() - elif row[person_join.c.type] =='manager': - return Manager() - else: - return Person() - - def populate_instance(self, mapper, session, instance, row, identitykey, imap, isnew): - if row[person_join.c.type] =='engineer': - Engineer.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) - return False - elif row[person_join.c.type] =='manager': - Manager.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) - return False - else: - return sqlalchemy.mapping.EXT_PASS - -people_mapper = mapper(Person, person_join, extension=PersonLoader()) - -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', status='AAB', manager_name='manager1')) -c.employees.append(Engineer(name='dilbert', status='BBA', engineer_name='engineer1', primary_language='java')) -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')) -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.engineer_name = '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() diff --git a/examples/polymorph/single.py b/examples/polymorph/single.py new file mode 100644 index 000000000..11455a590 --- /dev/null +++ b/examples/polymorph/single.py @@ -0,0 +1,86 @@ +from sqlalchemy import * + +metadata = BoundMetaData('sqlite://', echo='debug') + +# 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)) +) + +metadata.create_all() + +class Person(object): + def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): + 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.iteritems(): + 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') + + + +mapper(Company, companies, properties={ + 'employees': relation(Person, lazy=True, private=True, backref='company') +}) + +session = create_session() +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')) +session.save(c) +session.flush() + +session.clear() + +c = session.query(Company).get(1) +for e in c.employees: + print e, e._instance_key, e.company + +print "\n" + +dilbert = session.query(Person).get_by(name='dilbert') +dilbert2 = session.query(Engineer).get_by(name='dilbert') +assert dilbert is dilbert2 + +dilbert.engineer_name = 'hes dibert!' + +session.flush() +session.clear() + +c = session.query(Company).get(1) +for e in c.employees: + print e, e._instance_key + +session.delete(c) +session.flush() + +metadata.drop_all() |
