I've run into autoflush issue while using AssociationProxy with sqlalchemy-1.1.6. Appending to association list fails, because object is flushed before it is appended (and appending sets primary key value). I can avoid it with session.no_autoflush context manager, but I wonder if it's not worth slightly changing proxy behaviour to make it work with autoflush enabled.
Minimal example of this issue below: engine = engine_from_config(CONFIG) DBSession = scoped_session(sessionmaker()) DBSession.configure(bind=engine) Base = declarative_base() class UserAlias(Base): __tablename__ = 'user_aliases' user_id = Column(Integer, ForeignKey('user.id'), primary_key=True) alias_id = Column(Integer, ForeignKey('aliases.id'), primary_key=True) valid = Column(Boolean) alias = relationship('Alias', backref=backref('user_assocs', uselist= False)) user = relationship('User', backref=backref('alias_assocs')) def __init__(self, alias, valid=True): self.alias = alias self.valid = valid class Alias(Base): __tablename__ = 'aliases' id = Column(Integer, primary_key=True) name = Column('name', String(64)) user = association_proxy('user_assocs', 'user') def __init__(self, name): self.name = name class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String(64)) aliases = association_proxy('alias_assocs', 'alias') def __init__(self, name): self.name = name def test_assoc(): user = User(name='John') DBSession.add(user) DBSession.commit() johny = Alias(name='Johny') user.aliases.append(johny) DBSession.commit() jan = Alias(name='Jan') DBSession.add(jan) # in my real use case proxied object is already attached to session before appending, here I simulate it with DBSession.add user.aliases.append(jan) # autoflush fails here DBSession.commit() return user and stacktrace: /home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/sql /crud.py:692: SAWarning: Column 'user_aliases.user_id' is marked as a member of the primary key for table 'user_aliases', but has no Python-side or server-side default generator indicated, nor does it indicate 'autoincrement=True' or 'nullable=True', and no explicit value is passed. Primary key columns typically may not store NULL. Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be indicated explicitly for composite (e.g. multicolumn) primary keys if AUTO_INCREMENT/SERIAL/IDENTITY behavior is expected for one of the columns in the primary key. CREATE TABLE statements are impacted by this change as well on most backends. util.warn(msg) Traceback (most recent call last): File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/base.py" , line 1182, in _execute_context context) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/default.py" , line 470, in do_execute cursor.execute(statement, parameters) psycopg2.IntegrityError: null value in column "user_id" violates not-null constraint DETAIL: Failing row contains (null, 50, t). The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/home/bartek/dev/sqlalchemy-test/sqlalchemy_test/models.py", line 111, in <module> test_assoc() File "/home/bartek/dev/sqlalchemy-test/sqlalchemy_test/models.py", line 105, in test_assoc user.aliases.append(jan) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/ext/associationproxy.py" , line 610, in append self.col.append(item) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/ext/associationproxy.py" , line 509, in <lambda> col = property(lambda self: self.lazy_collection()) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/ext/associationproxy.py" , line 467, in __call__ return getattr(obj, self.target) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py" , line 237, in __get__ return self.impl.get(instance_state(instance), dict_) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py" , line 584, in get value = self.callable_(state, passive) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py" , line 557, in _load_for_state return self._emit_lazyload(session, state, ident_key, passive) File "<string>", line 1, in <lambda> File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py" , line 635, in _emit_lazyload result = q.all() File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/query.py" , line 2679, in all return list(self) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/query.py" , line 2830, in __iter__ self.session._autoflush() File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/session.py" , line 1375, in _autoflush util.raise_from_cause(e) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/util/compat.py" , line 203, in raise_from_cause reraise(type(exception), exception, tb=exc_tb, cause=cause) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/util/compat.py" , line 187, in reraise raise value File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/session.py" , line 1365, in _autoflush self.flush() File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/session.py" , line 2139, in flush self._flush(objects) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/session.py" , line 2259, in _flush transaction.rollback(_capture_exception=True) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/util/langhelpers.py" , line 60, in __exit__ compat.reraise(exc_type, exc_value, exc_tb) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/util/compat.py" , line 187, in reraise raise value File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/session.py" , line 2223, in _flush flush_context.execute() File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/unitofwork.py" , line 389, in execute rec.execute(self) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/unitofwork.py" , line 548, in execute uow File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/persistence.py" , line 181, in save_obj mapper, table, insert) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/persistence.py" , line 835, in _emit_insert_statements execute(statement, params) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/base.py" , line 945, in execute return meth(self, multiparams, params) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/sql/elements.py" , line 263, in _execute_on_connection return connection._execute_clauseelement(self, multiparams, params) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/base.py" , line 1053, in _execute_clauseelement compiled_sql, distilled_params File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/base.py" , line 1189, in _execute_context context) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/base.py" , line 1393, in _handle_dbapi_exception exc_info File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/util/compat.py" , line 203, in raise_from_cause reraise(type(exception), exception, tb=exc_tb, cause=cause) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/util/compat.py" , line 186, in reraise raise value.with_traceback(tb) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/base.py" , line 1182, in _execute_context context) File "/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/engine/default.py" , line 470, in do_execute cursor.execute(statement, parameters) sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (psycopg2.IntegrityError) null value in column "user_id" violates not-null constraint DETAIL: Failing row contains (null, 50, t). [SQL: 'INSERT INTO user_aliases (alias_id, valid) VALUES (%(alias_id)s, %(valid)s)'] [parameters: {'alias_id': 50, 'valid': True}] Autoflush occurs in _AssociationList append method: def append(self, value): item = self._create(value) self.col.append(item) If _create was called before self.col is evaluted created object would not be autoflushed prematurely. Maybe it is worth changing it if it does not break anything else. -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.