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 at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to