Hi, I think I have detected two bugs for PostgreSQL databases. I don't think is a different behavior between 0.6 and 0.7 as there is no problem with sqlite.
In the following code I have a very simple model, which contains a helper "pre-process" list class, which just transforms strings into the correct model object. Very similar to @collection.converter, however it works for append and extend. It works without errors (obviously w/o MutableComposite) in 0.6 and sqlite in 0.7. But the strange part is that it follows different behavior depending on when you add the parent object to the session: if you add it after creating the "company" (just like I put it here), then it will not insert the "data" object (see the produced log, if you change it to the sqlite engine it will work); but if you add and commit it, before and after the append operation it will fail with an IntegrityError in both sqlite and postgresql (this is why I doubt if it is a different behavior or not). Either way I think that adding the object, committing it, then adding it again, and committing it, although very wasteful, should behave without error, specially for occasions when you don't know how your object has been handled in relation with your database. The other bug is just for PostgreSQL and can be reproduced by uncommenting the "metadata.create_all()" line of code. It appears that is trying to create the data table before company. It doesn't matter in which order are defined the same error is raised. Thanks from sqlalchemy import create_engine, MetaData, Table, Column, Integer, \ String, ForeignKey from sqlalchemy.orm.collections import collection from sqlalchemy.orm import mapper, relationship, sessionmaker, composite from sqlalchemy.ext.mutable import MutableComposite engine = create_engine('postgresql://user:password@localhost:5432/db', echo=True) #engine = create_engine('sqlite:///:memory:', echo=True) metadata = MetaData() company = Table( 'company', metadata, Column('id', Integer, primary_key=True), Column('name', String, nullable=False), Column('x', Integer, nullable=False), Column('y', Integer, nullable=False), sqlite_autoincrement=True ) data = Table( 'data', metadata, Column('id', Integer, primary_key=True), Column('data', String, nullable=False), Column('company', Integer, ForeignKey('company.id'), nullable=False), sqlite_autoincrement=True ) metadata.bind = engine #metadata.drop_all() company.create() data.create() #metadata.create_all() class PreprocessList(list): def __init__(self, items=None): if items is None: super(PreprocessList, self).__init__() else: super(PreprocessList, self).__init__( [self.preprocess(item) for item in items] ) def preprocess(self, value): return value def __setitem__(self, key, value): value = self.preprocess(value) super(PreprocessList, self).__setitem__(key, value) @collection.internally_instrumented def append(self, value): value = self.preprocess(value) super(PreprocessList, self).append(value) @collection.internally_instrumented def extend(self, items): for item in items: self.append(item) class AppenderList(PreprocessList): def preprocess(self, value): v = Data() v.data = value + 'data' return v def __getitem__(self, key): value = super(AppenderList, self).__getitem__(key) return value.data class Data(object): pass class Company(object): pass class Point(MutableComposite): def __init__(self, x, y): self.x = x self.y = y def __setattr__(self, key, value): "Intercept set events" # set the attribute object.__setattr__(self, key, value) # alert all parents to the change self.changed() def __composite_values__(self): return self.x, self.y def __eq__(self, other): return isinstance(other, Point) and \ other.x == self.x and \ other.y == self.y def __ne__(self, other): return not self.__eq__(other) mapper(Company, company, properties={ 'id': company.c.id, 'name': company.c.name, 'data': relationship( Data, collection_class=AppenderList ), 'point': composite(Point, company.c.x, company.c.y) }) mapper(Data, data) session = sessionmaker(bind=engine)() # CHANGE BELOW c = Company() c.name = 'company 1' c.data = AppenderList() c.point = Point(3, 5) session.add(c) session.commit() c.data.append('a') #session.add(c) session.commit() metadata.drop_all() print 'ok' -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/9vkKkIpe9aEJ. 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.