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.