[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-17 Thread bojanb

 the solution is the same as that I illustrated in a previous email, that
 when you map to a JOIN you must place all equivalent columns which you
 would like populated identically in groups.  This is described 
 athttp://www.sqlalchemy.org/docs/05/mappers.html#mapping-a-class-agains...

You're right. Sorry, it just wasn't clear to me that you need to
specify the equivalent columns throughout the whole inheritance
hierarchy. I thoguht that SQLA would somehow automagically know that
they are equivalent.

Thanks a lot for your effort Mike, this was really of great help!

--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-17 Thread Michael Bayer

bojanb wrote:

 the solution is the same as that I illustrated in a previous email, that
 when you map to a JOIN you must place all equivalent columns which you
 would like populated identically in groups.  This is described
 athttp://www.sqlalchemy.org/docs/05/mappers.html#mapping-a-class-agains...

 You're right. Sorry, it just wasn't clear to me that you need to
 specify the equivalent columns throughout the whole inheritance
 hierarchy. I thoguht that SQLA would somehow automagically know that
 they are equivalent.

it figures that out when you use inherits, but mapping to the join
without using inherits it doesn't make any assumptions (since you could
be mapping to something more complicated and the equivalent logic would
start losing stability)


 Thanks a lot for your effort Mike, this was really of great help!

we're done, hooray !


--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-16 Thread bojanb

 with_polymorphic can be set against any subset of classes, not just '*'.

Yes, but in the first case I can't use with_polymorphic() on the
query, because the query class is not the problem - I want the
polymorphic load on an attribute (relation) of the queried class in
order for the eagerload to work. Therefore I must set with_polymorphic
in the mapper of that other class. But since mappers are global for
the whole application, I can't just set it to a subset of the classes
(then some other queries in the application won't work correctly).

 I don't have the time most of today to get into it so I can't confirm
 what's going on.  Any chance you could map to a straight join of all four
 tables instead of a join to two sub-joins ?

I'll try using single-table inheritance first, then I'll try that. It
would be great if you could look up into this when you have time. Just
run the last code example and SQL echo and error message should be
self-explanatory.

--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-16 Thread bojanb

 I don't have the time most of today to get into it so I can't confirm
 what's going on.  Any chance you could map to a straight join of all four
 tables instead of a join to two sub-joins ?

I tried the following join in the mapper for Subordinate:
join(Employee, Person).join(Relation,
onclause=Person.id==Relation.person_from_id).join(SupervisorRelation)

I also tried using single-table inheritance in order to avoid sub-
joins.

The results were the same - trying to select from a non-existing
sequnce. This time it's the sequence for the other object, so I guess
both are firing wrong, the exact error depends on which one is firing
first.

I could use MapperExtension.before_insert() as you suggested. However,
since rows are inserted first in parent table then in child table, I
would need to know that next primary key value and set the id to it.
But selecting from a sequnce causes it to increment, and the parent
and child primary keys will fail to be in sync. I would need to
somehow set the primary key after the parent object has been inserted
but before the child is inserted, and I don't know how to accomplish
that.
--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-16 Thread Michael Bayer

bojanb wrote:

 with_polymorphic can be set against any subset of classes, not just '*'.

 Yes, but in the first case I can't use with_polymorphic() on the
 query, because the query class is not the problem - I want the
 polymorphic load on an attribute (relation) of the queried class in
 order for the eagerload to work. Therefore I must set with_polymorphic
 in the mapper of that other class. But since mappers are global for
 the whole application, I can't just set it to a subset of the classes
 (then some other queries in the application won't work correctly).

not like I'll have time to get to this soon, but there *is* a ticket to
address this, #1106.


 I'll try using single-table inheritance first, then I'll try that. It
 would be great if you could look up into this when you have time. Just
 run the last code example and SQL echo and error message should be
 self-explanatory.

will do !


--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-16 Thread Michael Bayer

bojanb wrote:


 I hope this makes sense. Here's the code. When run, it throws
 ProgrammingError: (ProgrammingError) relation
 supervisor_relations_id_seq does not exist

the solution is the same as that I illustrated in a previous email, that
when you map to a JOIN you must place all equivalent columns which you
would like populated identically in groups.  This is described at
http://www.sqlalchemy.org/docs/05/mappers.html#mapping-a-class-against-multiple-tables
.

Here is a working mapper:

mapper(Subordinate, join(Employee, SupervisorRelation,
onclause=SupervisorRelation.person_from_id==Employee.id), properties={
   'relation_id': [relations.c.id, supervisor_relations.c.id], #Need
to rename asthere's also Employee.id
   'relation_type': relations.c.type, #Also need to rename
   'person_from_id': [relations.c.person_from_id, persons.c.id,
employees.c.id],

the effect of the error you're getting, the sequence not found, is a
separate issue which is ticket #1516, and that issue is fixed in 0.6.  If
you were to run your program with the broken mapping in 0.6, you'd instead
get null value in column id violates not-null constraint which is more
directly descriptive of the issue.



 code

 from sqlalchemy import create_engine, Table, Column, Integer, String,
 MetaData, ForeignKey
 from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload,
 join
 from sqlalchemy.orm.mapper import validates

 DB_URI='postgres://postg...@localhost/postgres' #Replace this
 accordingly
 db_engine=create_engine(DB_URI, echo=False)
 metadata = MetaData()

 class Person(object):

 def __init__(self, name):
 self.name = name

 persons = Table('persons',
 metadata,
 Column('id', Integer, primary_key=True),
 Column('type', String(1), nullable=False),
 Column('name', String(100), nullable=False))

 class Employee(Person):

 def __init__(self, name, position):
 Person.__init__(self, name)
 self.position = position

 employees = Table('employees',
   metadata,
   Column('id', Integer, ForeignKey('persons.id'),
 primary_key=True),
   Column('position', String(50), nullable=False))

 class Relation(object):

 def __init__(self, person_from, person_to):
 self.person_from = person_from
 self.person_to = person_to

 relations = Table('relations',
metadata,
Column('id', Integer, primary_key=True),
Column('type', String(1), nullable=False),
Column('person_from_id', Integer, ForeignKey
 ('persons.id'), nullable=False),
Column('person_to_id', Integer, ForeignKey
 ('persons.id'), nullable=False))

 class SupervisorRelation(Relation):
 def __init__(self, person_from, person_to, additional_info):
 Relation.__ini__(person_from, person_to)
 self.additional_info = additional_info

 supervisor_relations = Table('supervisor_relations',
   metadata,
   Column('id', Integer, ForeignKey
 ('relations.id'), primary_key=True),
   Column('additional_info', String(100),
 nullable=False))

 class Subordinate(object): #This class represents the business object
 that we work with
 pass

 mapper(Person, persons, polymorphic_on=persons.c.type,
 polymorphic_identity='P')
 mapper(Employee, employees, inherits=Person, polymorphic_identity='E')
 mapper(Relation, relations, polymorphic_on=relations.c.type,
 polymorphic_identity='R', properties={
'person_from': relation(Person, primaryjoin=
 (relations.c.person_from_id==persons.c.id)),
'person_to': relation(Person, primaryjoin=
 (relations.c.person_to_id==persons.c.id)),
})
 mapper(SupervisorRelation, supervisor_relations, inherits=Relation,
 polymorphic_identity='S')
 mapper(Subordinate, join(Employee, SupervisorRelation,
 onclause=SupervisorRelation.person_from_id==Employee.id), properties={
'relation_id': supervisor_relations.c.id, #Need to rename as
 there's also Employee.id
'relation_type': relations.c.type, #Also need to rename
'person_from_id': [relations.c.person_from_id, persons.c.id],
 #Need to declare them synonymous
'person_from': relation(Person, primaryjoin=
 (relations.c.person_from_id==persons.c.id)),
'person_to': relation(Person, primaryjoin=
 (relations.c.person_to_id==persons.c.id)),
})

 if __name__ == '__main__':
 metadata.create_all(db_engine)
 s=sessionmaker(bind=db_engine)()
 try:
 jack = Employee('Jack', 'manager')
 s.add(jack)
 s.commit()
 #Here we try to create a Subordinate object which should
 automatically create dependant objects
 db_engine.echo = True
 subordinate = Subordinate()
 subordinate.person_to = jack
 subordinate.name = 'Peter'
 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-15 Thread bojanb

The problem is when I have an object mapped against two tables, both
of which are part of an inheritance hierarchy. I managed to
synchronize the foreign key with the primary key (per the
documentation link you provided). However, SQLAlchemy doesn't (or I
can't instruct it how to) set the polymorphic discrimintaor fields
appropriately. I can set them manually, but then insert fails because
it looks for the sequence object on the inherited table, which doesn't
exist (it exist only on the root table of the inheritance hierarchy).

Here's example code. In brief, I have a Person-Employee and Relation-
SupervisorRelation as two independent inheritance hierarchies.
Relation is defined between two Persons, and SupervisorRelation
between two Employees. I want to hide this as an implementation and
have a Subordinate class that the programmer down the line will work
with. Subordinate contains fields from Employee and
SupervisorRelation. Querying on Subordinate works (efficiently, too),
and so does attribute modification. I would like to be able to create
it also (after populating the required fields and commit, the
underlying engine should create both a new Employee and a new
SupervisorRelation).

I hope this makes sense. Here's the code. When run, it throws
ProgrammingError: (ProgrammingError) relation
supervisor_relations_id_seq does not exist

code

from sqlalchemy import create_engine, Table, Column, Integer, String,
MetaData, ForeignKey
from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload,
join
from sqlalchemy.orm.mapper import validates

DB_URI='postgres://postg...@localhost/postgres' #Replace this
accordingly
db_engine=create_engine(DB_URI, echo=False)
metadata = MetaData()

class Person(object):

def __init__(self, name):
self.name = name

persons = Table('persons',
metadata,
Column('id', Integer, primary_key=True),
Column('type', String(1), nullable=False),
Column('name', String(100), nullable=False))

class Employee(Person):

def __init__(self, name, position):
Person.__init__(self, name)
self.position = position

employees = Table('employees',
  metadata,
  Column('id', Integer, ForeignKey('persons.id'),
primary_key=True),
  Column('position', String(50), nullable=False))

class Relation(object):

def __init__(self, person_from, person_to):
self.person_from = person_from
self.person_to = person_to

relations = Table('relations',
   metadata,
   Column('id', Integer, primary_key=True),
   Column('type', String(1), nullable=False),
   Column('person_from_id', Integer, ForeignKey
('persons.id'), nullable=False),
   Column('person_to_id', Integer, ForeignKey
('persons.id'), nullable=False))

class SupervisorRelation(Relation):
def __init__(self, person_from, person_to, additional_info):
Relation.__ini__(person_from, person_to)
self.additional_info = additional_info

supervisor_relations = Table('supervisor_relations',
  metadata,
  Column('id', Integer, ForeignKey
('relations.id'), primary_key=True),
  Column('additional_info', String(100),
nullable=False))

class Subordinate(object): #This class represents the business object
that we work with
pass

mapper(Person, persons, polymorphic_on=persons.c.type,
polymorphic_identity='P')
mapper(Employee, employees, inherits=Person, polymorphic_identity='E')
mapper(Relation, relations, polymorphic_on=relations.c.type,
polymorphic_identity='R', properties={
   'person_from': relation(Person, primaryjoin=
(relations.c.person_from_id==persons.c.id)),
   'person_to': relation(Person, primaryjoin=
(relations.c.person_to_id==persons.c.id)),
   })
mapper(SupervisorRelation, supervisor_relations, inherits=Relation,
polymorphic_identity='S')
mapper(Subordinate, join(Employee, SupervisorRelation,
onclause=SupervisorRelation.person_from_id==Employee.id), properties={
   'relation_id': supervisor_relations.c.id, #Need to rename as
there's also Employee.id
   'relation_type': relations.c.type, #Also need to rename
   'person_from_id': [relations.c.person_from_id, persons.c.id],
#Need to declare them synonymous
   'person_from': relation(Person, primaryjoin=
(relations.c.person_from_id==persons.c.id)),
   'person_to': relation(Person, primaryjoin=
(relations.c.person_to_id==persons.c.id)),
   })

if __name__ == '__main__':
metadata.create_all(db_engine)
s=sessionmaker(bind=db_engine)()
try:
jack = Employee('Jack', 'manager')
s.add(jack)
s.commit()
#Here we try to create a Subordinate object which should
automatically create dependant objects
db_engine.echo = True
subordinate = Subordinate()
subordinate.person_to = 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-15 Thread Michael Bayer

bojanb wrote:

 The problem is when I have an object mapped against two tables, both
 of which are part of an inheritance hierarchy. I managed to
 synchronize the foreign key with the primary key (per the
 documentation link you provided). However, SQLAlchemy doesn't (or I
 can't instruct it how to) set the polymorphic discrimintaor fields
 appropriately. I can set them manually, but then insert fails because
 it looks for the sequence object on the inherited table, which doesn't
 exist (it exist only on the root table of the inheritance hierarchy).

 Here's example code. In brief, I have a Person-Employee and Relation-
SupervisorRelation as two independent inheritance hierarchies.
 Relation is defined between two Persons, and SupervisorRelation
 between two Employees. I want to hide this as an implementation and
 have a Subordinate class that the programmer down the line will work
 with. Subordinate contains fields from Employee and
 SupervisorRelation. Querying on Subordinate works (efficiently, too),
 and so does attribute modification. I would like to be able to create
 it also (after populating the required fields and commit, the
 underlying engine should create both a new Employee and a new
 SupervisorRelation).


let me get this straight.   you want to map to a JOIN, which itself is
JOINed against two joined-table inheritance subclasses.

That is

 +--- join  --+
 ||
joinjoin

and each call to Query() would emit a JOIN against two sub-JOINs.

is this correct ?  is there a reason this need be so complex ?  (and its
efficient ? really ?  a query like that would bring any DB to a halt on a
large dataset, I would think...)

If the issue is just a sequence not firing off, an immediate workaround
would be to fire the sequence off yourself.  you can even do this in a
MapperExtension.before_insert() (good place for your polymorphic identity
setting too).  I don't as yet understand why the normal sequence firing
wouldn't be working here, is one firing off and the other not ?






 I hope this makes sense. Here's the code. When run, it throws
 ProgrammingError: (ProgrammingError) relation
 supervisor_relations_id_seq does not exist

 code

 from sqlalchemy import create_engine, Table, Column, Integer, String,
 MetaData, ForeignKey
 from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload,
 join
 from sqlalchemy.orm.mapper import validates

 DB_URI='postgres://postg...@localhost/postgres' #Replace this
 accordingly
 db_engine=create_engine(DB_URI, echo=False)
 metadata = MetaData()

 class Person(object):

 def __init__(self, name):
 self.name = name

 persons = Table('persons',
 metadata,
 Column('id', Integer, primary_key=True),
 Column('type', String(1), nullable=False),
 Column('name', String(100), nullable=False))

 class Employee(Person):

 def __init__(self, name, position):
 Person.__init__(self, name)
 self.position = position

 employees = Table('employees',
   metadata,
   Column('id', Integer, ForeignKey('persons.id'),
 primary_key=True),
   Column('position', String(50), nullable=False))

 class Relation(object):

 def __init__(self, person_from, person_to):
 self.person_from = person_from
 self.person_to = person_to

 relations = Table('relations',
metadata,
Column('id', Integer, primary_key=True),
Column('type', String(1), nullable=False),
Column('person_from_id', Integer, ForeignKey
 ('persons.id'), nullable=False),
Column('person_to_id', Integer, ForeignKey
 ('persons.id'), nullable=False))

 class SupervisorRelation(Relation):
 def __init__(self, person_from, person_to, additional_info):
 Relation.__ini__(person_from, person_to)
 self.additional_info = additional_info

 supervisor_relations = Table('supervisor_relations',
   metadata,
   Column('id', Integer, ForeignKey
 ('relations.id'), primary_key=True),
   Column('additional_info', String(100),
 nullable=False))

 class Subordinate(object): #This class represents the business object
 that we work with
 pass

 mapper(Person, persons, polymorphic_on=persons.c.type,
 polymorphic_identity='P')
 mapper(Employee, employees, inherits=Person, polymorphic_identity='E')
 mapper(Relation, relations, polymorphic_on=relations.c.type,
 polymorphic_identity='R', properties={
'person_from': relation(Person, primaryjoin=
 (relations.c.person_from_id==persons.c.id)),
'person_to': relation(Person, primaryjoin=
 (relations.c.person_to_id==persons.c.id)),
})
 mapper(SupervisorRelation, supervisor_relations, inherits=Relation,
 polymorphic_identity='S')
 mapper(Subordinate, join(Employee, 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-15 Thread bojanb

Yes, I want to map to a join between two classes which are parts of
joined table inheritance. I don't think it's complex - it fits very
naturally with the problem I am modeling.

When I said it's efficient, I meant that the generated SQL is optimal,
ie. the same as I would write if I were doing it by hand. eagerload
and with_polymorphic result in SQL that also queries on fields of
sibling classes (ie. which inherit from the same superclass but are
not in the inheritance path of the final class) which are unnecessary,
and contains a subquery (which I believe is not optimal).

My understanding is that performing joins on indexed fields is what
RDBMS do well. However, if the query turned out to be too slow I can
always switch to single-table inheritance - whether I use joined-table
or single-table inheritance is just an implementation detail (as I
understand it).

The problem is not that sequence is not firing off, it's that it's
firing for a sequence that doesn't exist. In the code above, it's
trying to get the next value from supervisor_relations_id sequence,
but that sequence doesn't exist because of inheritance. It should be
trying to get from relations_id_sequence but for some reason it
isn't. If you run the code you can see what's going on exactly in the
SQL echo.

I will play around with MapperExtension and single-table inheritance
and see what I get. However, I just thought that since selects and
updates work so nicely in this setup, create should also work in the
same way.

On Sep 15, 4:32 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 bojanb wrote:

  The problem is when I have an object mapped against two tables, both
  of which are part of an inheritance hierarchy. I managed to
  synchronize the foreign key with the primary key (per the
  documentation link you provided). However, SQLAlchemy doesn't (or I
  can't instruct it how to) set the polymorphic discrimintaor fields
  appropriately. I can set them manually, but then insert fails because
  it looks for the sequence object on the inherited table, which doesn't
  exist (it exist only on the root table of the inheritance hierarchy).

  Here's example code. In brief, I have a Person-Employee and Relation-
 SupervisorRelation as two independent inheritance hierarchies.
  Relation is defined between two Persons, and SupervisorRelation
  between two Employees. I want to hide this as an implementation and
  have a Subordinate class that the programmer down the line will work
  with. Subordinate contains fields from Employee and
  SupervisorRelation. Querying on Subordinate works (efficiently, too),
  and so does attribute modification. I would like to be able to create
  it also (after populating the required fields and commit, the
  underlying engine should create both a new Employee and a new
  SupervisorRelation).

 let me get this straight.   you want to map to a JOIN, which itself is
 JOINed against two joined-table inheritance subclasses.

 That is

      +--- join  --+
      |                    |
     join                join

 and each call to Query() would emit a JOIN against two sub-JOINs.

 is this correct ?  is there a reason this need be so complex ?  (and its
 efficient ? really ?  a query like that would bring any DB to a halt on a
 large dataset, I would think...)

 If the issue is just a sequence not firing off, an immediate workaround
 would be to fire the sequence off yourself.  you can even do this in a
 MapperExtension.before_insert() (good place for your polymorphic identity
 setting too).  I don't as yet understand why the normal sequence firing
 wouldn't be working here, is one firing off and the other not ?



  I hope this makes sense. Here's the code. When run, it throws
  ProgrammingError: (ProgrammingError) relation
  supervisor_relations_id_seq does not exist

  code

  from sqlalchemy import create_engine, Table, Column, Integer, String,
  MetaData, ForeignKey
  from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload,
  join
  from sqlalchemy.orm.mapper import validates

  DB_URI='postgres://postg...@localhost/postgres' #Replace this
  accordingly
  db_engine=create_engine(DB_URI, echo=False)
  metadata = MetaData()

  class Person(object):

      def __init__(self, name):
          self.name = name

  persons = Table('persons',
                  metadata,
                  Column('id', Integer, primary_key=True),
                  Column('type', String(1), nullable=False),
                  Column('name', String(100), nullable=False))

  class Employee(Person):

      def __init__(self, name, position):
          Person.__init__(self, name)
          self.position = position

  employees = Table('employees',
                    metadata,
                    Column('id', Integer, ForeignKey('persons.id'),
  primary_key=True),
                    Column('position', String(50), nullable=False))

  class Relation(object):

      def __init__(self, person_from, person_to):
          

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-15 Thread Michael Bayer

bojanb wrote:

 Yes, I want to map to a join between two classes which are parts of
 joined table inheritance. I don't think it's complex - it fits very
 naturally with the problem I am modeling.

 When I said it's efficient, I meant that the generated SQL is optimal,
 ie. the same as I would write if I were doing it by hand. eagerload
 and with_polymorphic result in SQL that also queries on fields of
 sibling classes (ie. which inherit from the same superclass but are
 not in the inheritance path of the final class) which are unnecessary,
 and contains a subquery (which I believe is not optimal).

with_polymorphic can be set against any subset of classes, not just '*'.


 My understanding is that performing joins on indexed fields is what
 RDBMS do well. However, if the query turned out to be too slow I can
 always switch to single-table inheritance - whether I use joined-table
 or single-table inheritance is just an implementation detail (as I
 understand it).

oh that query is going to be pretty slow for sure (though slow is a
relative term).


 The problem is not that sequence is not firing off, it's that it's
 firing for a sequence that doesn't exist. In the code above, it's
 trying to get the next value from supervisor_relations_id sequence,
 but that sequence doesn't exist because of inheritance. It should be
 trying to get from relations_id_sequence but for some reason it
 isn't. If you run the code you can see what's going on exactly in the
 SQL echo.

I don't have the time most of today to get into it so I can't confirm
what's going on.  Any chance you could map to a straight join of all four
tables instead of a join to two sub-joins ?







 I will play around with MapperExtension and single-table inheritance
 and see what I get. However, I just thought that since selects and
 updates work so nicely in this setup, create should also work in the
 same way.

 On Sep 15, 4:32 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 bojanb wrote:

  The problem is when I have an object mapped against two tables, both
  of which are part of an inheritance hierarchy. I managed to
  synchronize the foreign key with the primary key (per the
  documentation link you provided). However, SQLAlchemy doesn't (or I
  can't instruct it how to) set the polymorphic discrimintaor fields
  appropriately. I can set them manually, but then insert fails because
  it looks for the sequence object on the inherited table, which doesn't
  exist (it exist only on the root table of the inheritance hierarchy).

  Here's example code. In brief, I have a Person-Employee and Relation-
 SupervisorRelation as two independent inheritance hierarchies.
  Relation is defined between two Persons, and SupervisorRelation
  between two Employees. I want to hide this as an implementation and
  have a Subordinate class that the programmer down the line will work
  with. Subordinate contains fields from Employee and
  SupervisorRelation. Querying on Subordinate works (efficiently, too),
  and so does attribute modification. I would like to be able to create
  it also (after populating the required fields and commit, the
  underlying engine should create both a new Employee and a new
  SupervisorRelation).

 let me get this straight.   you want to map to a JOIN, which itself is
 JOINed against two joined-table inheritance subclasses.

 That is

      +--- join  --+
      |                    |
     join                join

 and each call to Query() would emit a JOIN against two sub-JOINs.

 is this correct ?  is there a reason this need be so complex ?  (and its
 efficient ? really ?  a query like that would bring any DB to a halt on
 a
 large dataset, I would think...)

 If the issue is just a sequence not firing off, an immediate workaround
 would be to fire the sequence off yourself.  you can even do this in a
 MapperExtension.before_insert() (good place for your polymorphic
 identity
 setting too).  I don't as yet understand why the normal sequence firing
 wouldn't be working here, is one firing off and the other not ?



  I hope this makes sense. Here's the code. When run, it throws
  ProgrammingError: (ProgrammingError) relation
  supervisor_relations_id_seq does not exist

  code

  from sqlalchemy import create_engine, Table, Column, Integer, String,
  MetaData, ForeignKey
  from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload,
  join
  from sqlalchemy.orm.mapper import validates

  DB_URI='postgres://postg...@localhost/postgres' #Replace this
  accordingly
  db_engine=create_engine(DB_URI, echo=False)
  metadata = MetaData()

  class Person(object):

      def __init__(self, name):
          self.name = name

  persons = Table('persons',
                  metadata,
                  Column('id', Integer, primary_key=True),
                  Column('type', String(1), nullable=False),
                  Column('name', String(100), nullable=False))

  class Employee(Person):

      def __init__(self, name, 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-14 Thread bojanb

The root of the problem is inheritance. Let's say that I have a Person
class and an Employee class that inherits from it. I also have a
Meeting class that records meetings between two persons.

A query on Meeting will always lazy load Employee's attributes,
regardless of any lazy/eagerload settings. E.g. if I want to print the
list of names of all persons somebody had meetings with and also their
position if they are employees (null if they're not), it will always
be done lazily. This is bad when I have, let's say, 100.000 Meetings.

I guess I can build a custom join and work from that, but if I have
two levels of inheritance on one side and three levels on the other
side, I will have to write a six-way join, and this, I'm sure you'll
agree, sort of defeats the purpose of an object-relational mapper.

Using classes mapped against multiple tables would elegantly solve
this problem, if I could only instantiate them (see my original post).

Here's the code that shows attributes of inherited objects are loaded
lazily:

code

from sqlalchemy import create_engine, Table, Column, Integer, String,
MetaData, ForeignKey
from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload
from sqlalchemy.orm.mapper import validates

DB_URI='postgres://postg...@localhost/postgres' #Replace this
accordingly
db_engine=create_engine(DB_URI, echo=False)
metadata = MetaData()

class Person(object):

def __init__(self, name):
self.name = name

persons = Table('persons',
metadata,
Column('id', Integer, primary_key=True),
Column('type', String(1), nullable=False),
Column('name', String(100), nullable=False))

class Employee(Person):

def __init__(self, name, position):
Person.__init__(self, name)
self.position = position

employees = Table('employees',
  metadata,
  Column('id', Integer, ForeignKey('persons.id'),
primary_key=True),
  Column('position', String(50), nullable=False))

class Meeting(object):

def __init__(self, date, person_from, person_to):
self.date = date
self.person_from = person_from
self.person_to = person_to

meetings = Table('meetings',
 metadata,
 Column('id', Integer, primary_key=True),
 Column('date', String(8), nullable=False),
 Column('person_from_id', Integer, ForeignKey
('persons.id'), nullable=False),
 Column('person_to_id', Integer, ForeignKey
('persons.id'), nullable=False))

mapper(Person, persons, polymorphic_on=persons.c.type,
polymorphic_identity='P')
mapper(Employee, employees, inherits=Person, polymorphic_identity='E')
mapper(Meeting, meetings, properties={
'person_from': relation(Person, primaryjoin=
(meetings.c.person_from_id==persons.c.id)),
'person_to': relation(Person, primaryjoin=
(meetings.c.person_to_id==persons.c.id)),
})

if __name__ == '__main__':
metadata.create_all(db_engine)
s=sessionmaker(bind=db_engine)()
try:
john = Person('John')
peter = Employee('Peter', 'clerk')
jack = Employee('Jack', 'manager')
m1 = Meeting('20090914', peter, john)
m2 = Meeting('20090915', peter, jack)
s.add_all([john, peter, jack, m1, m2])
s.commit()

db_engine.echo = True
#We now want to print the names and positions of everyone
Peter has ever met with
peters_meetings = s.query(Meeting).options(eagerload
('person_to')).filter_by(person_from=peter).all()
for meeting in peters_meetings:
if meeting.person_to.type == 'P':
print meeting.date, meeting.person_to.name, None
else:
#Each print statement here will emit an SQL SELECT on
the employees table
print meeting.date, meeting.person_to.name,
meeting.person_to.position

finally:
db_engine.echo = False
s.close()
metadata.drop_all(db_engine)

/code

On Sep 11, 7:52 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 have you tried using query + join() + contains_eager() ?  any query you
 like can be used to build the object graph of your choice along
 relations().



 bojanbwrote:

  Here's something I've been struggling with recently. I'll include the
  description of steps that got me here, as I believe the context will
  make the question clearer.

  It all started because I needed to show data (eg. in a list form) from
  two related tables (classes). However, SQLAlchemy would emit one SQL
  query for getting the objects of the first class, then one query each
  for each access to attributes of the other class. It obviously loads
  the attributes lazily, which is fine most of the time but grossly
  inefficient in this case (as there can be thousands of records in the
  first table).

  Aha, I'll use eagerload! I thought. Alas, it doesn't seem to work
  for inherited classes. A message 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-14 Thread Michael Bayer

bojanb wrote:

 Actually you can't use with_polymorphic() in the query because Meeting
 is not an inherited object (one would get an InvalidRequestError if
 one tried). But plugging:

 with_polymorphic='*'

 in the mapper for Person makes the eagerload work in the code above.

 However, we're off on a tangent. I still don't know how to instantiate
 objects of a class mapped against two tables when they contain both an
 autogenerated primary key from the first table and a mandatory foreign
 key from the second...

Just to clarify, the mapper on Employee with the with_polymorphic='*'
*is* a mapper that is mapped against two tables, in pretty much the same
way as a map against a plain join is represented.  So I'm assuming this is
unsuitable only because it's your observation that the joined tables in
your particular system are more of an implementation detail and you
don't really need to represent inheritance.

So, as far as synchronizing the foreign key with the primary key of the
two tables in a mapper that is mapped to a plain join, you just map two
columns to one attribute.  This is also in the docs, at
http://www.sqlalchemy.org/docs/05/mappers.html#mapping-a-class-against-multiple-tables
.   The tables are populated in order of foreign key dependency, and after
each primary key generation the value is synchronized to the mapped
attribute, where its then available for the insert into the second table.

Since I didn't read your initial (very long) email carefully enough, here
is your sample program using that style.

from sqlalchemy import create_engine, Table, Column, Integer,
String,MetaData, ForeignKey
from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload
from sqlalchemy.orm.mapper import validates

db_engine=create_engine('sqlite://', echo=True)
metadata = MetaData()

persons = Table('persons',
metadata,
Column('id', Integer, primary_key=True),
Column('name', String(100), nullable=False))

class Employee(object):

def __init__(self, name, position):
self.name = name
self.position = position

employees = Table('employees',
  metadata,
  Column('id', Integer,
ForeignKey('persons.id'),primary_key=True),
  Column('position', String(50), nullable=False))

class Meeting(object):

def __init__(self, date, person_from, person_to):
self.date = date
self.person_from = person_from
self.person_to = person_to

meetings = Table('meetings',
 metadata,
 Column('id', Integer, primary_key=True),
 Column('date', String(8), nullable=False),
 Column('person_from_id', Integer,
ForeignKey('persons.id'), nullable=False),
 Column('person_to_id', Integer, ForeignKey('persons.id'),
nullable=False))

mapper(Employee, employees.join(persons), properties={
'id':[persons.c.id, employees.c.id]
})

mapper(Meeting, meetings, properties={
'person_from': relation(Employee,
primaryjoin=(meetings.c.person_from_id==persons.c.id)),
'person_to': relation(Employee,
primaryjoin=(meetings.c.person_to_id==persons.c.id)),
})

if __name__ == '__main__':
metadata.create_all(db_engine)
s=sessionmaker(bind=db_engine)()

john = Employee('John', 'person')
peter = Employee('Peter', 'clerk')
jack = Employee('Jack', 'manager')
m1 = Meeting('20090914', peter, john)
m2 = Meeting('20090915', peter, jack)
s.add_all([john, peter, jack, m1, m2])
s.commit()

#We now want to print the names and positions of everyonePeter has
ever met with
peters_meetings =
s.query(Meeting).options(eagerload('person_to')).filter_by(person_from=peter).all()
for meeting in peters_meetings:
print meeting.date, meeting.person_to.name,meeting.person_to.position









 On Sep 14, 4:31 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 bojanb wrote:

  The root of the problem is inheritance. Let's say that I have a Person
  class and an Employee class that inherits from it. I also have a
  Meeting class that records meetings between two persons.

  A query on Meeting will always lazy load Employee's attributes,
  regardless of any lazy/eagerload settings. E.g. if I want to print the
  list of names of all persons somebody had meetings with and also their
  position if they are employees (null if they're not), it will always
  be done lazily. This is bad when I have, let's say, 100.000 Meetings.

  I guess I can build a custom join and work from that, but if I have
  two levels of inheritance on one side and three levels on the other
  side, I will have to write a six-way join, and this, I'm sure you'll
  agree, sort of defeats the purpose of an object-relational mapper.

  Using classes mapped against multiple tables would elegantly solve
  this problem, if I could only instantiate them (see my original post).

  Here's the code that shows attributes of inherited 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-14 Thread bojanb

Actually you can't use with_polymorphic() in the query because Meeting
is not an inherited object (one would get an InvalidRequestError if
one tried). But plugging:

with_polymorphic='*'

in the mapper for Person makes the eagerload work in the code above.

However, we're off on a tangent. I still don't know how to instantiate
objects of a class mapped against two tables when they contain both an
autogenerated primary key from the first table and a mandatory foreign
key from the second...

On Sep 14, 4:31 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 bojanb wrote:

  The root of the problem is inheritance. Let's say that I have a Person
  class and an Employee class that inherits from it. I also have a
  Meeting class that records meetings between two persons.

  A query on Meeting will always lazy load Employee's attributes,
  regardless of any lazy/eagerload settings. E.g. if I want to print the
  list of names of all persons somebody had meetings with and also their
  position if they are employees (null if they're not), it will always
  be done lazily. This is bad when I have, let's say, 100.000 Meetings.

  I guess I can build a custom join and work from that, but if I have
  two levels of inheritance on one side and three levels on the other
  side, I will have to write a six-way join, and this, I'm sure you'll
  agree, sort of defeats the purpose of an object-relational mapper.

  Using classes mapped against multiple tables would elegantly solve
  this problem, if I could only instantiate them (see my original post).

  Here's the code that shows attributes of inherited objects are loaded
  lazily:

 oh.  you want with_polymorphic() for this.

 http://www.sqlalchemy.org/docs/05/mappers.html#controlling-which-tabl...



  code

  from sqlalchemy import create_engine, Table, Column, Integer, String,
  MetaData, ForeignKey
  from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload
  from sqlalchemy.orm.mapper import validates

  DB_URI='postgres://postg...@localhost/postgres' #Replace this
  accordingly
  db_engine=create_engine(DB_URI, echo=False)
  metadata = MetaData()

  class Person(object):

      def __init__(self, name):
          self.name = name

  persons = Table('persons',
                  metadata,
                  Column('id', Integer, primary_key=True),
                  Column('type', String(1), nullable=False),
                  Column('name', String(100), nullable=False))

  class Employee(Person):

      def __init__(self, name, position):
          Person.__init__(self, name)
          self.position = position

  employees = Table('employees',
                    metadata,
                    Column('id', Integer, ForeignKey('persons.id'),
  primary_key=True),
                    Column('position', String(50), nullable=False))

  class Meeting(object):

      def __init__(self, date, person_from, person_to):
          self.date = date
          self.person_from = person_from
          self.person_to = person_to

  meetings = Table('meetings',
                   metadata,
                   Column('id', Integer, primary_key=True),
                   Column('date', String(8), nullable=False),
                   Column('person_from_id', Integer, ForeignKey
  ('persons.id'), nullable=False),
                   Column('person_to_id', Integer, ForeignKey
  ('persons.id'), nullable=False))

  mapper(Person, persons, polymorphic_on=persons.c.type,
  polymorphic_identity='P')
  mapper(Employee, employees, inherits=Person, polymorphic_identity='E')
  mapper(Meeting, meetings, properties={
          'person_from': relation(Person, primaryjoin=
  (meetings.c.person_from_id==persons.c.id)),
          'person_to': relation(Person, primaryjoin=
  (meetings.c.person_to_id==persons.c.id)),
          })

  if __name__ == '__main__':
      metadata.create_all(db_engine)
      s=sessionmaker(bind=db_engine)()
      try:
          john = Person('John')
          peter = Employee('Peter', 'clerk')
          jack = Employee('Jack', 'manager')
          m1 = Meeting('20090914', peter, john)
          m2 = Meeting('20090915', peter, jack)
          s.add_all([john, peter, jack, m1, m2])
          s.commit()

          db_engine.echo = True
          #We now want to print the names and positions of everyone
  Peter has ever met with
          peters_meetings = s.query(Meeting).options(eagerload
  ('person_to')).filter_by(person_from=peter).all()
          for meeting in peters_meetings:
              if meeting.person_to.type == 'P':
                  print meeting.date, meeting.person_to.name, None
              else:
                  #Each print statement here will emit an SQL SELECT on
  the employees table
                  print meeting.date, meeting.person_to.name,
  meeting.person_to.position

      finally:
          db_engine.echo = False
          s.close()
          metadata.drop_all(db_engine)

  /code

  On Sep 11, 7:52 pm, Michael Bayer mike...@zzzcomputing.com wrote:
  

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-14 Thread Michael Bayer

bojanb wrote:

 The root of the problem is inheritance. Let's say that I have a Person
 class and an Employee class that inherits from it. I also have a
 Meeting class that records meetings between two persons.

 A query on Meeting will always lazy load Employee's attributes,
 regardless of any lazy/eagerload settings. E.g. if I want to print the
 list of names of all persons somebody had meetings with and also their
 position if they are employees (null if they're not), it will always
 be done lazily. This is bad when I have, let's say, 100.000 Meetings.

 I guess I can build a custom join and work from that, but if I have
 two levels of inheritance on one side and three levels on the other
 side, I will have to write a six-way join, and this, I'm sure you'll
 agree, sort of defeats the purpose of an object-relational mapper.

 Using classes mapped against multiple tables would elegantly solve
 this problem, if I could only instantiate them (see my original post).

 Here's the code that shows attributes of inherited objects are loaded
 lazily:


oh.  you want with_polymorphic() for this.

http://www.sqlalchemy.org/docs/05/mappers.html#controlling-which-tables-are-queried






 code

 from sqlalchemy import create_engine, Table, Column, Integer, String,
 MetaData, ForeignKey
 from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload
 from sqlalchemy.orm.mapper import validates

 DB_URI='postgres://postg...@localhost/postgres' #Replace this
 accordingly
 db_engine=create_engine(DB_URI, echo=False)
 metadata = MetaData()

 class Person(object):

 def __init__(self, name):
 self.name = name

 persons = Table('persons',
 metadata,
 Column('id', Integer, primary_key=True),
 Column('type', String(1), nullable=False),
 Column('name', String(100), nullable=False))

 class Employee(Person):

 def __init__(self, name, position):
 Person.__init__(self, name)
 self.position = position

 employees = Table('employees',
   metadata,
   Column('id', Integer, ForeignKey('persons.id'),
 primary_key=True),
   Column('position', String(50), nullable=False))

 class Meeting(object):

 def __init__(self, date, person_from, person_to):
 self.date = date
 self.person_from = person_from
 self.person_to = person_to

 meetings = Table('meetings',
  metadata,
  Column('id', Integer, primary_key=True),
  Column('date', String(8), nullable=False),
  Column('person_from_id', Integer, ForeignKey
 ('persons.id'), nullable=False),
  Column('person_to_id', Integer, ForeignKey
 ('persons.id'), nullable=False))

 mapper(Person, persons, polymorphic_on=persons.c.type,
 polymorphic_identity='P')
 mapper(Employee, employees, inherits=Person, polymorphic_identity='E')
 mapper(Meeting, meetings, properties={
 'person_from': relation(Person, primaryjoin=
 (meetings.c.person_from_id==persons.c.id)),
 'person_to': relation(Person, primaryjoin=
 (meetings.c.person_to_id==persons.c.id)),
 })

 if __name__ == '__main__':
 metadata.create_all(db_engine)
 s=sessionmaker(bind=db_engine)()
 try:
 john = Person('John')
 peter = Employee('Peter', 'clerk')
 jack = Employee('Jack', 'manager')
 m1 = Meeting('20090914', peter, john)
 m2 = Meeting('20090915', peter, jack)
 s.add_all([john, peter, jack, m1, m2])
 s.commit()

 db_engine.echo = True
 #We now want to print the names and positions of everyone
 Peter has ever met with
 peters_meetings = s.query(Meeting).options(eagerload
 ('person_to')).filter_by(person_from=peter).all()
 for meeting in peters_meetings:
 if meeting.person_to.type == 'P':
 print meeting.date, meeting.person_to.name, None
 else:
 #Each print statement here will emit an SQL SELECT on
 the employees table
 print meeting.date, meeting.person_to.name,
 meeting.person_to.position

 finally:
 db_engine.echo = False
 s.close()
 metadata.drop_all(db_engine)

 /code

 On Sep 11, 7:52 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 have you tried using query + join() + contains_eager() ?  any query you
 like can be used to build the object graph of your choice along
 relations().



 bojanbwrote:

  Here's something I've been struggling with recently. I'll include the
  description of steps that got me here, as I believe the context will
  make the question clearer.

  It all started because I needed to show data (eg. in a list form) from
  two related tables (classes). However, SQLAlchemy would emit one SQL
  query for getting the objects of the first class, then one query each
  for each access to attributes of the other class. It obviously loads
  the attributes 

[sqlalchemy] Re: How to instantiate objects of a class mapped against multiple tables?

2009-09-11 Thread Michael Bayer

have you tried using query + join() + contains_eager() ?  any query you
like can be used to build the object graph of your choice along
relations().


bojanb wrote:

 Here's something I've been struggling with recently. I'll include the
 description of steps that got me here, as I believe the context will
 make the question clearer.

 It all started because I needed to show data (eg. in a list form) from
 two related tables (classes). However, SQLAlchemy would emit one SQL
 query for getting the objects of the first class, then one query each
 for each access to attributes of the other class. It obviously loads
 the attributes lazily, which is fine most of the time but grossly
 inefficient in this case (as there can be thousands of records in the
 first table).

 Aha, I'll use eagerload! I thought. Alas, it doesn't seem to work
 for inherited classes. A message in this group suggests that it
 doesn't work for self-referential inherited classes, but in this case
 it didn't work for plain inherited classes that don't contain
 references to self. I'll write a test case that shows this later.

 OK, I then decided to create a new class mapped against the two
 tables, using join() in a mapper. This worked great regarding the
 emitted SQL - session.query on new object generates the correct SQL
 even with the (deep) inheritance tree that we have. Modifying the
 attributes on of this object also writes them to correct respective
 tables on commit. Great! The new class even conceptually fits nicely
 with the rest of the application (e.g. I realized it's more of a
 business object while the two underlying classes/tables are more of an
 implementation detail; I'm not sure I even need the other two classes,
 just the tables may be enough). Fantastic!

 However, I can't figure how to create new instances of this (composite
 as I call it) class. Since it contains fields both for autogenerated
 primary key from the first class and foreign key from the second
 class, I cannot set the foreign key because I don't know the primary
 key before I commit, and commit fails because the foreign key is still
 null.

 Am I just missing something or am I attempting a no-no? I would think
 that since I've defined the attributes to be identical in the
 oncluase, SQLAlchemy would now that the two are dependent and would
 not complain about the null value.

 Of course, I can always create the two other objects, but being able
 to do it this way fits much more nicely with the rest of the
 application.
 



--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---