I only started learning SQLAlchemy a few days ago, so I apologize in advance if I'm just confused. :)

I've been thinking that I just didn't understand SQLAlchemy, but I now think I might be running into a nasty bug.  I've generated two simple tests (attached) that show the problem (one passes; the other fails).  Their only difference is in their schemas.  I think that the difference should be irrelevant to the tests, but it's not.

I define two tables, 'people' and 'email_addresses'.  Here's the schema for the test that passes:

        self.people_table = Table('people', self.metadata,
            Column('first_name', String, primary_key=True),
            Column('last_name', String, primary_key=True))

        self.email_addresses_table = Table('email_addresses', self.metadata,
            Column('person_first_name', String),
            Column('person_last_name', String),
            Column('email_address', String, primary_key=True),
            ForeignKeyConstraint(['person_first_name', 'person_last_name'],
                                 ['people.first_name', 'people.last_name' ]))

The actual test creates two people, one with an address and one without.  It then moves the address from one person to the other, and verifies that it was moved.  For the above schema, this works.  Here's what SQLAlchemy does when the address is switched:

    [2006-09-08 16:39:26,644] [engine]: BEGIN
    [2006-09-08 16:39:26,682] [engine]: UPDATE email_addresses SET person_first_name=%(person_first_name)s WHERE email_addresses.email_address = %(email_addresses_email_address)s
    [2006-09-08 16:39:26,684] [engine]: {'email_addresses_email_address': '[EMAIL PROTECTED]', 'person_first_name': 'jane'}
    [2006-09-08 16:39:26,688] [engine]: COMMIT

Now, here's the modified email_addresses table that makes it fail:

        self.email_addresses_table = Table('email_addresses', self.metadata,
            Column('person_first_name', String, primary_key=True),
            Column('person_last_name', String, primary_key=True),
            Column('email_address', String, primary_key=True),
            ForeignKeyConstraint(['person_first_name', 'person_last_name'],
                                 [' people.first_name', 'people.last_name']))

The only difference here is that the primary key is now composed of (first_name, last_name, email_address).  This doesn't really make sense semantically, but it illustrates the problem.  With this schema, and the same test as before, the email address never actually gets moved from one user to the other:

    [2006-09-08 16:39:26,910] [engine]: BEGIN
    [2006-09-08 16:39:26,912] [engine]: COMMIT

This is all with SQLAlchemy 0.2.8.  Is this expected behavior, or a known bug, or am I just lucky?
import unittest
from sqlalchemy import *

class Person(object):
    def __init__(self, first_name, last_name):
        self.first_name, self.last_name = first_name, last_name

class Email(object): 
    def __init__(self, email_address):
        self.email_address= email_address


class FKTestBase(unittest.TestCase):
    """
    Base class for the tests: contains the setup (except the tables) and the
    actual test.
    """

    def setUp(self):
        self.engine = create_engine('postgres://[EMAIL PROTECTED]/bitbacker')
        self.metadata = BoundMetaData(self.engine)

        self.people_table = Table('people', self.metadata, 
            Column('first_name', String, primary_key=True),
            Column('last_name', String, primary_key=True))
        self.define_email_table()

        # Mappers
        clear_mappers()
        mapper(Email, self.email_addresses_table)
        mapper(Person, self.people_table, properties=dict(email_addresses=relation(Email)))

        # Create the tables
        self.metadata.create_all()
        self.session = create_session(bind_to=self.engine)

    def tearDown(self):
        self.metadata.drop_all()

    def runTest(self):
        # Create john and give him an address
        john = Person('john', 'doe')
        self.session.save(john)
        john.email_addresses.append(Email('[EMAIL PROTECTED]'))
        self.session.flush()

        # Create jane with no addresses
        jane = Person('jane', 'doe')
        self.session.save(jane)
        self.session.flush()

        # Move the address from john to jane
        email = john.email_addresses[0]
        john.email_addresses.remove(email)
        jane.email_addresses.append(email)
        print
        self.engine.echo = True
        self.session.flush()
        self.engine.echo = False

        # Make sure that john has 0 addresses and jane has one
        self.session.refresh(john)
        self.session.refresh(jane)
        self.assertEquals(len(john.email_addresses), 0)
        self.assertEquals(len(jane.email_addresses), 1)


class FKTestPasses(FKTestBase):
    def define_email_table(self):
        self.email_addresses_table = Table('email_addresses', self.metadata,
            Column('person_first_name', String),
            Column('person_last_name', String),
            Column('email_address', String, primary_key=True),
            ForeignKeyConstraint(['person_first_name', 'person_last_name'],
                                 ['people.first_name', 'people.last_name']))



class FKTestFails(FKTestBase):
    def define_email_table(self):
        self.email_addresses_table = Table('email_addresses', self.metadata,
            Column('person_first_name', String, primary_key=True),
            Column('person_last_name', String, primary_key=True),
            Column('email_address', String, primary_key=True),
            ForeignKeyConstraint(['person_first_name', 'person_last_name'],
                                 ['people.first_name', 'people.last_name']))


if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTests([FKTestPasses(), FKTestFails()])
    unittest.TextTestRunner(verbosity=2).run(suite)

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users

Reply via email to