On Jan 17, 2007, at 5:09 PM, svil wrote:
110. can't make it less ;-)
can?
and it is about A=Employee and B=Manager, and all Employees having a
manager.
http://linuxteam.sistechnology.com/orm/sa_B_inh_A_A_ref_AB2.py

OK. the first attachment called "test_case1.py" is how I'd like you to send me these test cases. What you do is when you have your 110 lines, continue to refactor it down, removing conditionals and code until you have a *single* illustration of the failure. in this case it involved replacing superfluous objects with simple variable names, removing all the function enclosures (i.e. straight-down program execution), and factoring all the embedded hard-to-read conditionals into the single version that fails (in this case the "Manager" version is the one that fails). The attached case illustrates the problem succinctly and as it turns out is exactly 50 nonblank lines :). It still includes lots of things that are not part of the failure, such as all the "polymorphic" arguments, but I can handle a little bit of extra fluff.

From that point, we have a category of issue that comes up all the time, where what you want to do is possible, but SA never expected exactly what youre doing and therefore does not report the problem its having in any useful way.

the reason its not working is because the relationship between "Employee" and "Manager" has a join condition where each column in the join is a foreign key to one or the other side of the relationship - so neither the direction of the relationship, nor the proper column to populate, can be determined from the join condition alone. SA then incorrectly determines which column in the join is the "foreign" column and gets all confused later on. If you now try this test program with the trunk, youll get as clear as an error as I could come up with: "Cant determine relation direction for 'manager' on mapper 'Mapper|Employee|Employee' with primary join '"Employee".manager_id = "Manager".id' - foreign key columns are present in both the parent and the child's mapped tables. Specify 'foreignkey' argument."

So one way that this would work, and what SA normally expected here, is that when you make a relation between Employee and Manager, SA would see that as a self-referential relationship between Employee and Employee; if you made your ForeignKey on the "employee" table point to "employee.id" instead of "manager.id", and you also update your "primaryjoin" condition accordingly, the mappers detect a self- referential condition and then it works:

                Column('manager_id', Integer,
                    ForeignKey( 'Employee.id',
                       use_alter=True, name='whatever1'
                    )
            )

But it works the way you have it as well. If you just provide a "foreignkey" parameter to the join condition, limiting which columns in the join you'd like it to consider to be the "foreignkey" that is significant in this join, it also works:

        properties={
'manager':relation(Manager, primaryjoin=employee_table.c.manager_id==manager_table.c.id, foreignkey=employee_table.c.manager_id, uselist=False, post_update=True)
        }

and that is what is attached in working_version.py.

The unfortunate thing about "foreignkey" is that it is kind of a messy parameter which is a little inconsistent, namely that it isnt used for many-to-many relationships right now, and I plan on replacing with something more comprehensive in the future (ticket 385) so that it can totally replace the usage of ForeignKey on Table (and make life easier for MySQL folks who like to reflect their tables). In fact until I saw this example I didnt think there was ever a reason you'd need to use "foreignkey" right now provided the Table's had proper ForeignKeys on them (self-referential mappers use "remote_side" now).



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

class Employee( object):
    name = '<notset>'
    def __str__(me):
        return ' '.join( [me.__class__.__name__, str(me.id),str(me.name), getattr( me.manager, 'name', '<none>') ])
        
class Manager( Employee ):
    bonus = '<notset>'
    def __str__(me):
        return Employee.__str__(me) + ' ' + str(me.bonus)

db = create_engine( 'sqlite:///:memory:')
meta = BoundMetaData( db)
meta.engine.echo = 0

class tables: pass

employee_table = Table('Employee', meta,
        Column('id', Integer, primary_key=True),
        Column('name', String, ),
        Column('atype', String),
        Column('manager_id', Integer,
                    ForeignKey( 'Manager.id',
                       use_alter=True, name='whatever1'
                    )
            )
    )

manager_table = Table('Manager', meta,
        Column('bonus', String, ),
        Column('id', Integer,
                    ForeignKey( 'Employee.id'),
                    primary_key=True,
            ),
)
meta.create_all()

ajoin = {
    'Employee': employee_table.select( employee_table.c.atype =='Employee'),
    'Manager': join( employee_table, manager_table,manager_table.c.id ==employee_table.c.id),
}

Ajoin = polymorphic_union( ajoin, None )

mA = mapper( Employee, employee_table,
        select_table=Ajoin, polymorphic_on=Ajoin.c.atype,
        polymorphic_identity='Employee',
        properties={
            'manager':relation(Manager, primaryjoin=employee_table.c.manager_id==manager_table.c.id, post_update=True, uselist=False)
        }
)

mB = mapper( Manager, manager_table,
           polymorphic_identity='Manager',
        inherits = mA,
        inherit_condition = (employee_table.c.id ==manager_table.c.id),
)

#populate
session = create_session()

a = Employee()
a.name = 'Dilberto'
b = Manager()
b.name = 'Boss'
b.bonus = 'big'

a.manager = b

session.save(a)
session.save(b)

session.flush()
from sqlalchemy import *

class Employee( object):
    name = '<notset>'
    def __str__(me):
        return ' '.join( [me.__class__.__name__, str(me.id),str(me.name), getattr( me.manager, 'name', '<none>') ])
        
class Manager( Employee ):
    bonus = '<notset>'
    def __str__(me):
        return Employee.__str__(me) + ' ' + str(me.bonus)

db = create_engine( 'sqlite:///:memory:')
meta = BoundMetaData( db)
meta.engine.echo = 1

class tables: pass

employee_table = Table('Employee', meta,
        Column('id', Integer, primary_key=True),
        Column('name', String, ),
        Column('atype', String),
        Column('manager_id', Integer,
                    ForeignKey( 'Manager.id',
                       use_alter=True, name='whatever1'
                    )
            )
    )

manager_table = Table('Manager', meta,
        Column('bonus', String, ),
        Column('id', Integer,
                    ForeignKey( 'Employee.id'),
                    primary_key=True,
            ),
)
meta.create_all()

ajoin = {
    'Employee': employee_table.select( employee_table.c.atype =='Employee'),
    'Manager': join( employee_table, manager_table,manager_table.c.id ==employee_table.c.id),
}

Ajoin = polymorphic_union( ajoin, None )

mA = mapper( Employee, employee_table,
        select_table=Ajoin, polymorphic_on=Ajoin.c.atype,
        polymorphic_identity='Employee',
        properties={
            'manager':relation(Manager, primaryjoin=employee_table.c.manager_id==manager_table.c.id, foreignkey=employee_table.c.manager_id, uselist=False, post_update=True)
        }
)

mB = mapper( Manager, manager_table,
           polymorphic_identity='Manager',
        inherits = mA,
        inherit_condition = (employee_table.c.id ==manager_table.c.id),
)

#populate
session = create_session()

a = Employee()
a.name = 'Dilberto'
b = Manager()
b.name = 'Boss'
b.bonus = 'big'

a.manager = b

session.save(a)
session.save(b)

session.flush()

session.clear()
a = session.query(Employee).get(a.id)
b = session.query(Manager).get(b.id)
assert a.name == 'Dilberto'
assert b.name == 'Boss'
assert b.bonus == 'big'
assert a.manager is b



Reply via email to