Hi,

I am using SQLAlchemy 0.6.4 with postgres db. I have two tables - users and
addresses tables with addresses table having a foreign key constraint
referencing the users table. Each address record is identified by a unique
constraint key 'email_address'.
In my test case, each user instance have a collection of addresses. For each
user instance, I want to delete every address instance in the addresses
collection that the user instance has and then add new address instances
(they may have the same unique key that the previously deleted address
instance had). The problem I am having now is that at the end of the flush
call, unique constraint error for 'email_address' from addresses table is
thrown even though delete operation is done earlier than insert. Looking at
the echo output, INSERTs are indeed done first than DELETEs. The work around
that I have now is to call flush() right after the deletion of address
instances in each user.

My question is - what is the precedence of insert, delete and update in
session flush? It would also be helpful if someone can explain the overview
of the mechanics of flushing in SQLAlchemy.
Attached is the python script that I wrote to understand why this problem
mentioned above happens.

Thanks.

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

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import Column, UniqueConstraint, ForeignKeyConstraint
from sqlalchemy.types import String, Integer
from sqlalchemy.orm import sessionmaker, relationship, backref
from sqlalchemy import create_engine

engine = create_engine('postgres://postgres:data01@localhost:5432/flush_test')

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    __table_args__ = (
            UniqueConstraint('name',
                name='users_name_key'),
            {})
    id = Column(Integer, primary_key=True)
    name = Column(String(20), nullable=False)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<User('%s')>" % self.name

class Address(Base):
    __tablename__ = 'addresses'
    __table_args__ = (
                UniqueConstraint('email_address',
                    name='addresses_email_address_key'),
                ForeignKeyConstraint(['username'],
                                     ['users.name'],
                                     onupdate='cascade'),
                {}
            )
    id = Column(Integer, primary_key=True)
    email_address = Column(String(20), nullable=False)
    username = Column(String(20), nullable=False)
    user = relationship(User, backref=backref('addresses', cascade='all, delete-orphan'))

    def __init__(self, email_address):
        self.email_address = email_address
    def __repr__(self):
        return "<Address('%s')>" % self.email_address

metadata = Base.metadata
metadata.create_all(engine)
Session = sessionmaker(bind=engine, autoflush=False)

session = Session()
session.rollback()

# begin init code that puts the persisted instance into the database
if session.query(User).filter_by(name='user1').count()==0:
    user1 = User('user1')
    user1.addresses.append(Address('us...@email.com'))
    session.add(user1)
    session.commit()

else:
    user1=session.query(User).filter_by(name='user1').one()
    if len(user1.addresses)==0:
        user1.addresses.append(Address('us...@email.com'))
    session.commit()
# end init code

engine.echo = True

for address in user1.addresses:
    session.delete(address)

user1.addresses.append(Address('us...@email.com'))

# After the flush line below unique constraint error for address table is thrown,
# echo output shows that insertion of child item was done first before delete, although we call delete first.
# This does not happen if we flush after delete.
session.flush() 

Reply via email to