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'
>         subordinate.position = 'clerk'
>         subordinate.additional_info = 'Works for Jack since 2007'
>         subordinate.type = 'E'
>         subordinate.relation_type='S'
>         s.add(subordinate)
>         s.commit() #Fails
>
>     finally:
>         db_engine.echo = False
>         s.close()
>         metadata.drop_all(db_engine)
>
> </code>
>
>
>  Here's the code that demonstrates the problem from my original
> question (the previous code was in response to your reply which
>
> On Sep 14, 6:52 pm, "Michael Bayer" <mike...@zzzcomputing.com> wrote:
>> bojanbwrote:
>>
>> > 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,
>> athttp://www.sqlalchemy.org/docs/05/mappers.html#mapping-a-class-agains...
>> .   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=pete­r).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:
>> >>bojanbwrote:
>>
>> >> > 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:
>> >> >> 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
>>
>> ...
>>
>> read more »
> >
>


--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to