Hi,

I've ran into a problem when using a composite primary key with
auto_incremented values and nested relations. My scenario is as
follows:
I have three models A, B and C, where A has a one-to-many relation to
B and B has a one-to-many relation to C.
A has a standard primarykey consisting of one integer column that is
autoincremented.
B has a combined primary key using the foreign key to A.id as the
first part and another autoincremented id column as the second part of
the key.
Finally, C has a combined primary key consisting of the A.id and B.id
and another autoincremented id column.

I create an instance of A, add a new instance of B to the "b"-
attribute of A and then call add the A-instance to the session and
call commit().
SQLAlchemy correctly sets the autogenerated ID value of A on the B-
instance and saves everything to the database, so if the A instance
gets an ID of 1, the a_id-column in the B-table is populated
correctly.
So far, nothing unusual.

Now I repeat the first use case but I also append a brand new instance
of C to the "c"-attribute of the B instance (my object graph looks
something like this: A.b[B.c[C]]). When I add the A instance to the
session and call commit(), the A and B models are correctly saved like
before.
However, the primary key on the C model is broken: SQLAlchemy does set
the a_id-column value of the combined key but does not set the
autoincremented value of the B.id column on the b_id-column
(Ironically, SQLAlchemy logs a warning message, that the column b_id
does not have a default value).
Is this a bug in the SQLAlchemy mapper or is there something I can do
about it ?

I've created a small test case to demonstrate the behaviour (using
Mysql):

import logging
import sqlalchemy
from sqlalchemy import *
from sqlalchemy.orm import *

metadata = MetaData()
table_a = Table('table_a', metadata,
    Column('id', Integer(), primary_key=True),
    Column('name', String(20), nullable=True),
)

table_b = Table('table_b', metadata,
    Column('a_id', Integer(), nullable=False, primary_key=True),
    Column('id', Integer(), nullable=False, primary_key=True),
    Column('name', String(20), nullable=True),
    ForeignKeyConstraint(['a_id'], ['table_a.id'],
ondelete='CASCADE'),
)

table_c = Table('table_c', metadata,
    Column('a_id', Integer(), nullable=False, primary_key=True),
    Column('b_id', Integer(), nullable=False, primary_key=True),
    Column('id', Integer(), primary_key=True),
    Column('name', String(20), nullable=True),
    ForeignKeyConstraint(['a_id', 'b_id'], ['table_b.a_id',
'table_b.id'], ondelete='CASCADE'),
)

# Models
class ItemA(object):
    def __init__(self, name):
        self.name = name

class ItemB(object):
    def __init__(self, name):
        self.name = name

class ItemC(object):
    def __init__(self, name):
        self.name = name

# Mapping
mapper(ItemA, table_a, properties={'b': relation(ItemB, backref="a",
cascade="all, delete-orphan")})
mapper(ItemB, table_b, properties={'c': relation(ItemC, backref="b",
cascade="all, delete-orphan")})
mapper(ItemC, table_c)

# Init engine
engine = create_engine('mysql://localhost/sqltest', connect_args=
{'user':'root', 'passwd':''}, echo=True)
engine_logger = sqlalchemy.log.instance_logger(engine)
engine_logger.setLevel(logging.DEBUG)
metadata.drop_all(engine)
metadata.create_all(engine)
session_factory = scoped_session(sessionmaker(bind=engine))
session = session_factory()

itemA1 = ItemA(name = 'ItemA1')
itemB1 = ItemB(name = 'ItemB1')
itemB1.c.append(ItemC(name = 'ItemC1'))
itemA1.b.append(itemB1)
session.add(itemA1)
session.commit()

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