On Oct 18, 4:06 pm, Michael Bayer <[email protected]> wrote:
> On Oct 18, 2010, at 2:46 PM, Jasper K wrote:
>
>
>
> > Hi Group,
>
> > I am using SqlAlchemy 0.5.8
>
> > I get an "(OperationalError) no such column: child.child_id" error
> > when trying to eagerload a viewonly many-to-many relationship that
> > uses the Association Object pattern with a custom secondaryjoin
> > condition (http://www.sqlalchemy.org/docs/05/mappers.html#association-
> > object).
>
> > The following example demonstrates the issue. It consists of three
> > tables: Parent, Child, Association. The goal is to have a mapped
> > property on the Parent table that filters out certain associations
> > based on properties stored in the Child table (the Parent is trying to
> > eagerload all it's "nice children"). It seems the SQL that gets
> > generated during the eagerload fails to include the Child table.
> > However when used without the eagerload everything works as expected.
> > I have not tried this with the newer SqlAlchemy but I would like to
> > get it working on 0.5.8 if possible.
>
> So "secondary" and "secondaryjoin" only apply to this exact pattern:
>
> Parent -> association table -> Child
>
> your example is doing this:
>
> Parent -> association table -> Association Object
>
> The correct mapping, using "secondary", would be:
>
> mapper(Parent, parent_table, properties={
> 'nice_children': relation(Child, lazy=True,
> secondary=association_table,
>
>
> primaryjoin=parent_table.c.parent_id==association_table.c.parent_id,
>
>
> secondaryjoin=and_(association_table.c.child_id==child_table.c.child_id,
> child_table.c.status==NICE),
> viewonly=True)
> })
>
> If you'd like to maintain that "nice_children" returns "Association" objects,
> you'd have to get the link to "child" to be referenced inside the
> primaryjoin, as the endpoint of the JOIN must be the target of the relation():
>
> mapper(Parent, parent_table, properties={
> 'nice_children': relation(Association, lazy=True,
> primaryjoin=and_(
> parent_table.c.parent_id==association_table.c.parent_id,
>
> association_table.c.child_id.in_(select([child_table.c.child_id]).where(child_table.c.status==NICE))
> ),
> viewonly=True)
> })
>
Yes, I would like to maintain the Association Object pattern since
there is other data that is stored in the association table.
This second approach works well, thanks for your help.
>
>
> > Any help is much appreciated,
>
> > Thanks
>
> > from sqlalchemy import Table, Column, String, Integer, MetaData,
> > ForeignKey, create_engine, and_
> > from sqlalchemy.orm import mapper, relation, sessionmaker, eagerload
>
> > engine = create_engine('sqlite:///:memory:', echo=True)
> > metadata = MetaData()
> > Session = sessionmaker(bind=engine)
> > session = Session()
>
> > NICE = 'NICE'
> > NAUGHTY = 'NAUGHTY'
>
> > parent_table = Table('parent', metadata,
> > Column('parent_id', Integer, primary_key=True),
> > Column('name', String(32)))
>
> > child_table = Table('child', metadata,
> > Column('child_id', Integer, primary_key=True),
> > Column('name', String(32)),
> > Column('status', String(32)))
>
> > association_table = Table('association', metadata,
> > Column('id', Integer, primary_key=True),
> > Column('parent_id', ForeignKey('parent.parent_id')),
> > Column('child_id', ForeignKey('child.child_id')))
>
> > class Parent(object):
> > def __init__(self, name):
> > self.name = name
>
> > class Child(object):
> > def __init__(self, name, status):
> > self.name = name
> > self.status = status
>
> > class Association(object):
> > def __init__(self, parent, child):
> > self.parent = parent
> > self.child = child
>
> > mapper(Parent, parent_table, properties={
> > 'nice_children': relation(Association, lazy=True,
> > secondary=association_table,
>
> > primaryjoin=parent_table.c.parent_id==association_table.c.parent_id,
>
> > secondaryjoin=and_(association_table.c.child_id==child_table.c.child_id,
> > child_table.c.status==NICE),
> > viewonly=True)
> > })
>
> > mapper(Association, association_table, properties={
> > 'child': relation(Child),
> > 'parent': relation(Parent)
> > })
>
> > mapper(Child, child_table)
>
> > metadata.create_all(engine)
>
> > # CREATE ALL THE OBJECTS
>
> > jack = Parent('Jack')
> > kid1 = Child('Kid1', NICE)
> > kid2 = Child('Kid2', NAUGHTY)
>
> > session.add_all([jack, kid1, kid2])
> > session.flush()
>
> > assoc1 = Association(jack, kid1)
> > assoc2 = Association(jack, kid2)
>
> > session.add_all([assoc1, assoc2])
> > session.flush()
>
> > assert jack.nice_children
>
> > session.expunge_all()
>
> > # TEST THE EAGERLOADING
>
> > parents = session.query(Parent).all()
> > assert parents
> > assert parents[0].nice_children[0].child.name == 'Kid1'
>
> > # fails
> > parents =
> > session.query(Parent).options(eagerload('nice_children')).all()
> > assert parents
> > assert parents[0].nice_children[0].child.name == 'Kid1'
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "sqlalchemy" group.
> > To post to this group, send email to [email protected].
> > To unsubscribe from this group, send email to
> > [email protected].
> > For more options, visit this group
> > athttp://groups.google.com/group/sqlalchemy?hl=en.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected].
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.