On Feb 25, 2011, at 11:59 AM, Chris Withers wrote:

> Hi All,
> 
> With these models:
> 
> class User(Base):
>    __tablename__ = 'user'
>    __table_args__ = {'mysql_engine':'InnoDB'}
>    username = Column(String(50), primary_key=True)
>    grants = dynamic_loader("Grant")
> 
> class Grant(Base):
> 
>    __tablename__ = 'grant'
>    __table_args__ = {'mysql_engine':'InnoDB'}
>    username = Column(String(50),
>                      ForeignKey('user.username',ondelete='cascade'),
>                      primary_key=True)
>    user = relation("User",cascade="all")
> 
> And an autoflush=True, autocommit=False, Session backed onto a MySQL 
> database, if I do:
> 
> user = User(username='testname',
>            password='testpassword')
> session.add(user)
> session.add(Grant(user=user)))
> assert session.query(User).count()
> 
> ...then no SQL is executed until the query, and at that point, the order of 
> execution of the two insert statements appears random, such that sometimes 
> the grant is inserted first, resulting in an IntegrityError as the foreign 
> key constraint fails.

can't reproduce.  See attached.    This test includes randomization of all key 
data structures in the UOW which smokes out any issues in dependency sorting.




> 
> What am I doing wrong here and why is the order of the SQL emitted apparently 
> arbitrary?
> 
> Now, if it does succeed, and I go on to do:
> 
> session.delete(session.query(User).get('testname'))
> assert session.query(User).count()==0
> 
> When the count is executed, I get:
> 
>    assert session.query(User).count()==0
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/query.py",
>  line 2010, in count
>    should_nest = should_nest[0]
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/query.py",
>  line 2045, in _col_aggregate
>    self.session._autoflush()
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/session.py",
>  line 862, in _autoflush
>    self.flush()
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/session.py",
>  line 1388, in flush
>    self._flush(objects)
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/session.py",
>  line 1469, in _flush
>    flush_context.execute()
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/unitofwork.py",
>  line 302, in execute
>    rec.execute(self)
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/unitofwork.py",
>  line 402, in execute
>    self.dependency_processor.process_deletes(uow, states)
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/dependency.py",
>  line 488, in process_deletes
>    uowcommit, False)
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/dependency.py",
>  line 532, in _synchronize
>    sync.clear(dest, self.mapper, self.prop.synchronize_pairs)
>  File 
> "/home/chris/buildout-eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/sync.py",
>  line 41, in clear
>    (r, mapperutil.state_str(dest))
> AssertionError: Dependency rule tried to blank-out primary key column 
> 'grant.username' on instance '<Grant at 0x9ed946c>'
> 
> ...which is confusing. Why doesn't the cascade on the Grant.user relation 
> stop this happening?
> 
> cheers,
> 
> Chris
> 
> -- 
> Simplistix - Content Management, Batch Processing & Python Consulting
>           - http://www.simplistix.co.uk
> 
> -- 
> 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 import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
import random

class RandomSet(set):
    def __iter__(self):
        l = list(set.__iter__(self))
        random.shuffle(l)
        return iter(l)

    def pop(self):
        index = random.randint(0, len(self) - 1)
        item = list(set.__iter__(self))[index]
        self.remove(item)
        return item

    def union(self, other):
        return RandomSet(set.union(self, other))

    def difference(self, other):
        return RandomSet(set.difference(self, other))

    def intersection(self, other):
        return RandomSet(set.intersection(self, other))

    def copy(self):
        return RandomSet(self)

def reverse_top():
    from sqlalchemy.orm import unitofwork, session, mapper, dependency
    from sqlalchemy import topological
    topological.set = unitofwork.set = session.set = mapper.set = dependency.set = RandomSet

reverse_top()

Base  = declarative_base()

class User(Base):
   __tablename__ = 'user'
   __table_args__ = {'mysql_engine':'InnoDB'}
   username = Column(String(50), primary_key=True)
   grants = dynamic_loader("Grant")

class Grant(Base):

   __tablename__ = 'grant'
   __table_args__ = {'mysql_engine':'InnoDB'}
   username = Column(String(50),
                     ForeignKey('user.username',ondelete='cascade'),
                     primary_key=True)
   user = relation("User",cascade="all")

e = create_engine('mysql://scott:tiger@localhost/test', echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)


session = Session(e)

user = User(username='testname')
session.add(user)
session.add(Grant(user=user))
assert session.query(User).count()

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