[sqlalchemy] Re: polymorphic_identity determination

2006-11-08 Thread Michael Bayer


On Nov 8, 2006, at 12:04 AM, Randall Smith wrote:


 That leads to the part I'm stuck on; mapper inheritance.  When  
 finished,
 session.query(Employee).select() should list all employee as instances
 of their specific classes and session.query(Engineer).select() should
 list all engineers ...  So how do I set up the mappers to accomplish
 this?  The polymorphic_on/polymorphic_identity method seems to only
 accommodate 1 level of inheritance.  The Engineer mapper will inherit
 from the Employee mapper, but will have no polymorphic_identity.


beacuse the polymorphic union is manually constructed, if you want  
multi-level loading you have to construct individual polymorphic  
unions for each class from which you want to load polymorphically,  
and specify it as the select_table argument for each mapper that  
requires polymorphic loading.

when you say query(Employee).select(), it uses the mapper assigned to  
Employee and the select_table which you specified.

when you say query(Engineer).select(), the Employee mapper is not  
used; it uses the Engineer mapper and the select_table specified on  
*that* mapper.

it *is* in the cards that the polymorphic_union query will probably  
become more automated in a future release.  the polymorphic_union  
function itself is the first version of this automation and im sort  
of road testing just that level of automation to start with.

for your other questions:

1. a mapper inheriting from another mapper is mostly significant with  
regards to the persistence of object instances, i.e. during flush(),  
since there is an entire set of dependencies between inheriting  
classes.  with regards to selecting and populating, an inherited  
mapper inherits the properties of the parent mapper, i.e. the column- 
mapped attributes as well as the relation()'s which you have attached  
to the parent mapper.  when you add the polymorphic_identity into  
the mix, an additional set of logic occurs during the _instance()  
method on mapper, which is the method that receives a row and  
produces an object instance.  it just looks at the polymorphic_on  
column in each incoming row and delegates the instance construction  
to the appropriate mapper.

so the loading of polymorphic entities can be totally understood by  
just looking at the queries issued, and then looking at the rows that  
come back.  each row needs some kind of identifying type in order to  
determine the class to be instantiated.

2. the concrete flag refers to an inheriting mapper that stores its  
entire set of data in its own table, and does not need any columns  
from the parent mapper's table in order to map an instance.  so to  
load a particular class of entity involves querying only one table,  
and no join is required.  this flag is only meaningful in inheritance  
relationships.  I just noticed that one of my docstrings in the  
constructor of the Mapper class is incorrect regarding local_table  
and concrete as well, in case that was confusing you. it should refer  
to single table inheritance.

3. select_table should be used when you want polymorphic loading from  
a particular mapper, and the inheritance relationship spans over more  
tables than just what is defined in that mapper.  this means you use  
it for joined table and concrete inheritance, but not single table.   
if a set of mappers mixes the approaches, then youd probably want to  
use select_table.



--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-07 Thread Randall Smith

I worked on this some more and found that the queries are correct as 
they are.  The problem is in the mapping.  A Chemical Engineer is an 
Engineer, who is an Employee.  So the employees selectable should return 
all employees and the engineer selectable should return all engineers, 
not just unspecific engineers.

For the purpose of discussion and example, assume all engineers must be 
specified.  They may be chemical or mechanical engineers.  I think the 
polymorphic union should look like this:

p_union = polymorphic_union(
 {
 'ac': accountants,
 'me': mechanical_engineers,
 'ce': chemical_engineers,
 },
 type,
)

Notice that I didn't include the employee table nor the engineer 
selectable.  That's because I don't want instances of the Employee class 
or the Engineer class.  They would cause redundant data since a Chemical 
Engineer is an Engineer is an Employee, etc.

That leads to the part I'm stuck on; mapper inheritance.  When finished, 
session.query(Employee).select() should list all employee as instances 
of their specific classes and session.query(Engineer).select() should 
list all engineers ...  So how do I set up the mappers to accomplish 
this?  The polymorphic_on/polymorphic_identity method seems to only 
accommodate 1 level of inheritance.  The Engineer mapper will inherit 
from the Employee mapper, but will have no polymorphic_identity.

Summary of design:

* Single employees table with ac_info, ce_info, and me_info columns.
* Selectables for each employee type that looks like this:
 engineers = select([employees, ],
 and_(employees.c.engineer_info != None,
 employees.c.cheme_info==None)).alias('engineers')
 chemical_engineers = select([employees,
 column('ce').label('type')],
 and_(employees.c.engineer_info != None,
 employees.c.ce_info != None)).alias('chemical_engineers')
* Polymorphic Union that only includes types for instances we want
   (No Employee or Engineer) types.

So how could mappers be set up to accommodate these criteria?

Randall

Michael Bayer wrote:
 if you change your echo to 'debug', or just select straight from your
 p_union selectable, youll see these rows:
 
 (5, u'cengineer1', u'cengineer1', u'cengineer1', None,
 u'chemical_engineer')
 (6, u'cengineer2', u'cengineer2', u'cengineer2', None,
 u'chemical_engineer')
 (1, u'manager1', None, None, u'manager1', u'manager')
 (2, u'manager2', None, None, u'manager2', u'manager')
 (3, u'engineer1', u'engineer1', None, None, u'engineer')
 (4, u'engineer2', u'engineer2', None, None, u'engineer')
 (5, u'cengineer1', u'cengineer1', u'cengineer1', None, u'engineer')
 (6, u'cengineer2', u'cengineer2', u'cengineer2', None, u'engineer')
 
 Where you can see that the chemical engineers are coming out twice with
 inconsistent types.  the query has to be tuned to be more specific:
 
 managers = select([employees, column('manager').label('type')],
 employees.c.manager_data !=
None).alias('managers')
 engineers = select([employees, column('engineer').label('type')],
 and_(employees.c.engineer_info !=
 None,
 employees.c.cheme_info==None)).alias('engineers')
 chemical_engineers = select([employees,
 column('chemical_engineer').label('type')],
  and_(employees.c.engineer_info != None,
   employees.c.cheme_info !=
 None)).alias(
   'chemical_engineers')
 
 
 p_union = polymorphic_union(
 {
 'engineer': engineers,
 'manager': managers,
 'chemical_engineer': chemical_engineers
 },
 None, 
 )


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-06 Thread Michael Bayer

hey, no, youre doing great, breaking new ground yourself.  i havent
spent much time with inheritance scenarios other than what you see in
the examples and unittests (and a lot of that other people came up
with).   just as long as the rowsets contain the information, things
are good.  this is also why i have the polymorphic union as a
separate, manually-configured query - I dont trust that an automated
system of generating those is going to be smart enough just yet.


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-05 Thread Randall Smith
I've attached a file which is a variant to the employees example with 
two objectives.

1. Base polymorphic_identity on select criteria (no type column).
2. Use two levels of inheritance.

The first objective seems to be met, but the second is not working 
properly.  I put in two Managers, two Generic Engineers and two Chemical 
Engineers (which inherit from Engineer).  When I select from employees, 
I get eight records.  The Chemical Engineers are included once as 
Chemical Engineers and once and Generic Engineers.

How might this be better written to meet these objectives?

Randall

Michael Bayer wrote:
 just FYI, the type column idea is taken from Hibernate, and that's
 all Hibernate supports as far as polymorphic loading.
 
 But for polymorphic loading in SA, you are free to make any kind of
 polymorphic_union you like that can add in a functionally-generated
 type column, and specify it into select_table.  im pretty sure this
 should work completely right now, such as:
 
 import sqlalchemy.sql as sql
 
 person_join = polymorphic_union(
 {
   'engineer':sql.select([people.join(engineers),
 sql.column('engineer').label('type')]),
   'manager':sql.select([people.join(managers),
 sql.column('manager').label('type')]),
}
 )
 
 etc.
 
 
  
 



--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---
from sqlalchemy import *

metadata = DynamicMetaData('testdata')

employees = Table('employees', metadata, 
Column('employee_id', Integer, primary_key=True),
Column('name', String(50)),
Column('manager_data', String(50)),
Column('engineer_info', String(50)),
Column('cheme_info', String(50)),
)

managers = select([employees], employees.c.manager_data !=
   None).alias('managers')
engineers = select([employees], employees.c.engineer_info !=
None).alias('engineers')
chemical_engineers = select([employees],
 and_(employees.c.engineer_info != None,
  employees.c.cheme_info != None)).alias(
  'chemical_engineers')

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

class ChemicalEngineer(Engineer):
def __init__(self, name, engineer_info, cheme_info):
self.name = name
self.engineer_info = engineer_info
self.cheme_info = cheme_info

def __repr__(self):
return self.__class__.__name__ +   + self.name +   +  self.engineer_info

p_union = polymorphic_union(
{
'engineer': engineers,
'manager': managers,
'chemical_engineer': chemical_engineers
},
'type'
)

employee_mapper = mapper(Employee, p_union, polymorphic_on=p_union.c.type)
manager_mapper = mapper(Manager, managers, inherits=employee_mapper,
concrete=True, polymorphic_identity='manager')
engineer_mapper = mapper(Engineer, engineers, inherits=employee_mapper,
concrete=True, polymorphic_identity='engineer')
mapper(ChemicalEngineer, chemical_engineers, inherits=engineer_mapper,
   concrete=True, polymorphic_identity='chemical_engineer')

def populate(session):
m1 = Manager('manager1', 'manager1') 
m2 = Manager('manager2', 'manager2') 
e1 = Engineer('engineer1', 'engineer1') 
e2 = Engineer('engineer2', 'engineer2') 
ce1 = ChemicalEngineer('cengineer1', 'cengineer1', 'cengineer1') 
ce2 = ChemicalEngineer('cengineer2', 'cengineer2', 'cengineer2') 
for o in (m1, m2, e1, e2, ce1, ce2):
session.save(o)
session.flush()

if __name__ == '__main__':
engine = create_engine('sqlite:///test.db')
engine.echo = True
metadata.connect(engine)
metadata.drop_all()
metadata.create_all()
session = create_session(engine)
populate(session)
print session.query(Employee).select()


[sqlalchemy] Re: polymorphic_identity determination

2006-11-05 Thread Michael Bayer

if you change your echo to 'debug', or just select straight from your
p_union selectable, youll see these rows:

(5, u'cengineer1', u'cengineer1', u'cengineer1', None,
u'chemical_engineer')
(6, u'cengineer2', u'cengineer2', u'cengineer2', None,
u'chemical_engineer')
(1, u'manager1', None, None, u'manager1', u'manager')
(2, u'manager2', None, None, u'manager2', u'manager')
(3, u'engineer1', u'engineer1', None, None, u'engineer')
(4, u'engineer2', u'engineer2', None, None, u'engineer')
(5, u'cengineer1', u'cengineer1', u'cengineer1', None, u'engineer')
(6, u'cengineer2', u'cengineer2', u'cengineer2', None, u'engineer')

Where you can see that the chemical engineers are coming out twice with
inconsistent types.  the query has to be tuned to be more specific:

managers = select([employees, column('manager').label('type')],
employees.c.manager_data !=
   None).alias('managers')
engineers = select([employees, column('engineer').label('type')],
and_(employees.c.engineer_info !=
None,
employees.c.cheme_info==None)).alias('engineers')
chemical_engineers = select([employees,
column('chemical_engineer').label('type')],
 and_(employees.c.engineer_info != None,
  employees.c.cheme_info !=
None)).alias(
  'chemical_engineers')


p_union = polymorphic_union(
{
'engineer': engineers,
'manager': managers,
'chemical_engineer': chemical_engineers
},
None, 
)


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-05 Thread John M Camara


Randall Smith wrote:
 For discussion, consider the Employee, Manager, Engineer example from
 the docs.  If I were designing the tables, I would not normally have a
 type field.  I would use the null status of engineer_info or
 manager_data to determine the employee type.  Or if that data was in
 separate tables, I would use the existence of records in those tables.
 And these are simple cases.  I can think of some real life cases where
 the values of multiple fields would determine the class.

IMO you always need to keep your people/place/things data separate from
your roles data.  If you put the 2 together you end up developing a
system that is highly inflexible and the code will start to look ugly.
After all a user can wear many hats.  He can be an Employee, Manager,
Engineer, etc.  He can even be an Employee of 2 different companies, a
manager of multiple groups, etc.

So I would keep user tables/classes separate from role tables/classes.
For the database design you would have 2 basic choices.

First option:

User Table
  userID
  ...
Role Table
  userID
  roleType
  roleData
  ...

Where roleData would likely contain serialized (pickled) data.

Second option:

User Table
  id
  ...
Role Table
  id
  userID
  roleType
  roleDetailsID
  ...
Role1Details Table
  id
  ...
Role2Details Table
  id
  ...
other role details tables

I would only go with the second option if you actually needed to do ad
hoc queries of the Role#Details tables.  Otherwise, the first option is
far quicker to code and provides more flexibility.

John


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-05 Thread Randall Smith

John,

Thanks for the feedback.  The code I put up is not part of any real 
system.  I'm just playing off of the existing examples in the docs and 
trying to get comfortable with SA inheritance.

Randall

John M Camara wrote:
 
 Randall Smith wrote:
 For discussion, consider the Employee, Manager, Engineer example from
 the docs.  If I were designing the tables, I would not normally have a
 type field.  I would use the null status of engineer_info or
 manager_data to determine the employee type.  Or if that data was in
 separate tables, I would use the existence of records in those tables.
 And these are simple cases.  I can think of some real life cases where
 the values of multiple fields would determine the class.
 
 IMO you always need to keep your people/place/things data separate from
 your roles data.  If you put the 2 together you end up developing a
 system that is highly inflexible and the code will start to look ugly.
 After all a user can wear many hats.  He can be an Employee, Manager,
 Engineer, etc.  He can even be an Employee of 2 different companies, a
 manager of multiple groups, etc.
 
 So I would keep user tables/classes separate from role tables/classes.
 For the database design you would have 2 basic choices.
 
 First option:
 
 User Table
   userID
   ...
 Role Table
   userID
   roleType
   roleData
   ...
 
 Where roleData would likely contain serialized (pickled) data.
 
 Second option:
 
 User Table
   id
   ...
 Role Table
   id
   userID
   roleType
   roleDetailsID
   ...
 Role1Details Table
   id
   ...
 Role2Details Table
   id
   ...
 other role details tables
 
 I would only go with the second option if you actually needed to do ad
 hoc queries of the Role#Details tables.  Otherwise, the first option is
 far quicker to code and provides more flexibility.
 
 John
 
 
  
 


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-04 Thread Michael Bayer

just FYI, the type column idea is taken from Hibernate, and that's
all Hibernate supports as far as polymorphic loading.

But for polymorphic loading in SA, you are free to make any kind of
polymorphic_union you like that can add in a functionally-generated
type column, and specify it into select_table.  im pretty sure this
should work completely right now, such as:

import sqlalchemy.sql as sql

person_join = polymorphic_union(
{
  'engineer':sql.select([people.join(engineers),
sql.column('engineer').label('type')]),
  'manager':sql.select([people.join(managers),
sql.column('manager').label('type')]),
   }
)

etc.


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: polymorphic_identity determination

2006-11-04 Thread Randall Smith

Michael Bayer wrote:
 just FYI, the type column idea is taken from Hibernate, and that's
 all Hibernate supports as far as polymorphic loading.

I think it's good that SA takes good features from and is similar to 
Hibernate, but I hope that your aspirations and those of SA's users are 
to make SA much better than Hibernate.  I think Python vs. Java/XML (I 
despise the XML modeling) is already a boost for SA.  SA is amazing and 
I've got high hopes for its future.

 
 But for polymorphic loading in SA, you are free to make any kind of
 polymorphic_union you like that can add in a functionally-generated
 type column, and specify it into select_table.  im pretty sure this
 should work completely right now, such as:
 
 import sqlalchemy.sql as sql
 
 person_join = polymorphic_union(
 {
   'engineer':sql.select([people.join(engineers),
 sql.column('engineer').label('type')]),
   'manager':sql.select([people.join(managers),
 sql.column('manager').label('type')]),
}
 )
 
 etc.
 

Thanks for code example.  I suspect that's the approach I'll be using 
most often.

Randall


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---