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