Great, thanks a lot!

On Feb 17, 7:05 pm, Michael Bayer <mike...@zzzcomputing.com> wrote:
> the stack trace tells all for autoflush situations.  Note this is an 0.7 
> stacktrace, 0.6 is slightly different but the same series of steps:
>
>   File "test.py", line 107, in <module>
>     group = Group([item1, item2])
>   File "<string>", line 4, in __init__
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/state.py", line 105, 
> in initialize_instance
>     return manager.original_init(*mixed[1:], **kwargs)
>
>  File "test.py", line 68, in __init__
> ----> 1.      self.items_by_owner[item.owner] = item
>
>   File 
> "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/ext/associationproxy.py", line 
> 600, in __setitem__
>     self.col[key] = self._create(key, value)
>
>   File 
> "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/ext/associationproxy.py", line 
> 585, in _create
> ----> 2.     return self.creator(key, value)
>
>   File "test.py", line 62, in create_for_proxy
> ---> 3.     return cls(None, owner, item)
>
>  File "<string>", line 4, in __init__
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/state.py", line 105, 
> in initialize_instance
>     return manager.original_init(*mixed[1:], **kwargs)
>
>   File "test.py", line 57, in __init__
> ---> 4, 5.    self.item = item
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 149, in __set__
>     instance_dict(instance), value, None)
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 642, in set
>     value = self.fire_replace_event(state, dict_, value, old, initiator)
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 662, in fire_replace_event
>     value = fn(state, value, previous, initiator or self)
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 932, in set_
> ---> 6.    passive=PASSIVE_NO_FETCH)
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 436, in append
>     self.set(state, dict_, value, initiator, passive=passive)
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 638, in set
> ----> 7.    old = self.get(state, dict_, passive=PASSIVE_ONLY_PERSISTENT)
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 
> 414, in get
>     value = self.callable_(state, passive)
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/strategies.py", line 
> 542, in _load_for_state
> ---> 8.     result = q.all()
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/query.py", line 
> 1636, in all
>     return list(self)
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/query.py", line 
> 1745, in __iter__
>     self.session._autoflush()
>
>   File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 
> 901, in _autoflush
> ---> 9.    self.flush()
>
> 1. items_by_owner assignment
> 2. association proxy receives assignment, calls creator
> 3. creator is GroupOwner.create_for_proxy
> 4. GroupOwner constructor
> 5. assign self.item =item
> 6. group_owners backref must assign
> 7. group_owners backref is uselist=False, old value must be placed in the 
> "deleted" collection since new value replaces.  "deleted" collection is so 
> session knows to assign NULL to the item's foreign key.
> 8. Item.group_owners is not present in __dict__.  Old value must be loaded.   
> There's no old value in this case, but SQLA doesn't know that until it loads
> 9. autoflush
>
> Solution 1:
>
> initialize group_owners to None:
>
> class Item(object):
>    def __init__(self, name, owner):
>        self.name = name
>        self.owner = owner
>        self.group_owners = None
>
> solution 2:
>
> disable autoflush in the GroupOwner constructor.  
> Seehttp://www.sqlalchemy.org/trac/wiki/UsageRecipes/DisableAutoflush.
>
> On Feb 17, 2011, at 5:56 AM, Julien Demoor wrote:
>
> > Hi,
>
> > I'm running into a problem illustrated by the code below. The result
> > is an IntegrityError:
>
> > sqlalchemy.exc.IntegrityError: (IntegrityError) null value in column
> > "group_id" violates not-null constraint
> > 'INSERT INTO groups_owners (owner_id, item_id) VALUES (%(owner_id)s, %
> > (item_id)s) RETURNING groups_owners.group_id' {'item_id': 1,
> > 'owner_id': 1}
>
> > Looking at the stack trace, autoflush is triggerred by the assignments
> > in GroupOwner.__init__(), but I fail to see why or what to do about
> > it.
>
> > The error appeared when I set a backref with uselist=False on the
> > GroupOwner.item relationship. I can work around the problem by using a
> > list instead, although that would make less sense since there's at
> > most one group per item. Is there an error in my relationship
> > configuration?
>
> > I'm running SA 0.6.6.
>
> > ==== Code to reproduce the error ====
>
> > from sqlalchemy.ext.associationproxy import association_proxy
> > from sqlalchemy.orm.collections import attribute_mapped_collection
> > from sqlalchemy import Table, Column, Integer, Numeric, String,
> > ForeignKey, MetaData
> > from sqlalchemy.engine import create_engine
> > from sqlalchemy.orm import sessionmaker, mapper, relationship, backref
>
> > meta = MetaData()
> > engine = create_engine('postgresql://test:test@localhost/test')
> > Session = sessionmaker(bind=engine)
> > session = Session()
>
> > owners = Table("owners", meta,
> >               Column('id', Integer, primary_key=True),
> >               Column('name', String)
> > )
>
> > items = Table("items", meta,
> >              Column('id', Integer, primary_key=True),
> >              Column('name', String),
> >              Column('owner_id', Integer, ForeignKey('owners.id'))
> > )
>
> > groups = Table("groups", meta,
> >               Column('id', Integer,primary_key=True),
> > )
>
> > groups_owners = Table("groups_owners", meta,
> >                      Column('group_id', Integer,
> > ForeignKey('groups.id'), primary_key=True),
> >                      Column('owner_id', Integer,
> > ForeignKey('owners.id'), primary_key=True),
> >                      Column('item_id', Integer,
> > ForeignKey('items.id'), nullable=False, unique=True) # notice
> > unique=True; items can belong to one group at most
> > )
>
> > def _create_holding(stock, shares):
> >    """A creator function, constructs Holdings from Stock and share
> > quantity."""
> >    return Holding(stock=stock, shares=shares)
>
> > class Owner(object):
> >    def __init__(self, name):
> >        self.name = name
>
> >    holdings = association_proxy('by_stock', 'shares',
> > creator=_create_holding)
>
> > class Item(object):
> >    def __init__(self, name, owner):
> >        self.name = name
> >        self.owner = owner
>
> > class GroupOwner(object):
> >    def __init__(self, group, owner, item):
> >        self.group = group
> >        self.owner = owner
> >        self.item = item
> >        #assert item.owner is owner
>
> >    @classmethod
> >    def create_for_proxy(cls, owner, item):
> >        return cls(None, owner, item)
>
> > class Group(object):
> >    def __init__(self, items):
> >        if items:
> >            for item in items:
> >                self.items_by_owner[item.owner] = item
>
> >    items_by_owner = association_proxy('group_owners', 'item',
> >                                 creator=GroupOwner.create_for_proxy)
>
> > mapper(Owner, owners)
> > mapper(Item, items, properties={
> >    'owner': relationship(Owner),
> > })
> > mapper(Group, groups, properties={
> >    'group_owners': relationship(GroupOwner,
> > collection_class=attribute_mapped_collection('owner')),
> > })
> > mapper(GroupOwner, groups_owners, properties={
> >    'group': relationship(Group),
> >    # Fails
> >    'item': relationship(Item, backref=backref('group_owners',
> > uselist=False), single_parent=True),
> >    # Doesn't fail
> >    #'item': relationship(Item, backref=backref('group_owners'),
> > single_parent=True),
> >    'owner': relationship(Owner),
> > })
>
> > meta.drop_all(bind=engine, checkfirst=True)
> > meta.create_all(bind=engine)
>
> > owner1 = Owner('owner 1')
> > item1 = Item('item 1', owner1)
>
> > owner2 = Owner('owner 2')
> > item2 = Item('item 2', owner2)
>
> > session.add(owner1)
> > session.add(owner2)
> > session.add(item1)
> > session.add(item2)
>
> > session.flush()
>
> > # Fails:
> > group = Group([item1, item2])
> > # Doesn't fail
> > #group = Group([item1])
>
> > session.add(group)
> > session.commit()
>
> > assert session.query(GroupOwner).filter_by(group_id=1, item_id=1,
> > owner_id=1).first() is not None
> > print 'Done.'
>
> > --
> > 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 
> > athttp://groups.google.com/group/sqlalchemy?hl=en.
>
>

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