that's not a bad idea at all can you transfer this to a bug report at https://bitbucket.org/zzzeek/sqlalchemy/issues?status=new&status=open ? that would be helpful.


On 03/17/2017 09:11 AM, bartwojci...@gmail.com wrote:
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()


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


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


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


deftest_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()
    returnuser
|

and stacktrace:

|
/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/sql/crud.py:692:SAWarning:Column'user_aliases.user_id'ismarked
asa member of the primary key fortable 'user_aliases',but has
noPython-side orserver-side defaultgenerator indicated,nor does it
indicate 'autoincrement=True'or'nullable=True',andnoexplicitvalue
ispassed. Primarykey columns typically may notstore NULL.Notethat asof
SQLAlchemy1.1,'autoincrement=True'must be indicated explicitly
forcomposite (e.g.multicolumn)primary keys
ifAUTO_INCREMENT/SERIAL/IDENTITY behavior isexpected forone of the
columns inthe primary key.CREATE TABLE statements are impacted
bythischange aswell 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,indo_execute
    cursor.execute(statement,parameters)
psycopg2.IntegrityError:nullvalue incolumn "user_id"violates
not-nullconstraint
DETAIL: Failingrow contains (null,50,t).


Theabove 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,intest_assoc
    user.aliases.append(jan)

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/ext/associationproxy.py",line
610,inappend
    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(lambdaself:self.lazy_collection())

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/ext/associationproxy.py",line
467,in__call__
    returngetattr(obj,self.target)

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py",line
237,in__get__
    returnself.impl.get(instance_state(instance),dict_)

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py",line
584,inget
    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
    returnself._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,inall
    returnlist(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,inraise_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,inreraise
    raisevalue

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,inflush
    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,inreraise
    raisevalue

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,inexecute
    rec.execute(self)

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/unitofwork.py",line
548,inexecute
    uow

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/orm/persistence.py",line
181,insave_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,inexecute
    returnmeth(self,multiparams,params)

File"/home/bartek/envs/sqlalchemy-test/lib/python3.5/site-packages/sqlalchemy/sql/elements.py",line
263,in_execute_on_connection
    returnconnection._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,inraise_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,inreraise
    raisevalue.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,indo_execute
    cursor.execute(statement,parameters)
sqlalchemy.exc.IntegrityError:(raised asa result of Query-invoked
autoflush;consider usinga session.no_autoflush block ifthisflush
isoccurring prematurely)(psycopg2.IntegrityError)nullvalue incolumn
"user_id"violates not-nullconstraint
DETAIL: Failingrow 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:

|
    defappend(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
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto:sqlalchemy@googlegroups.com>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

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

Reply via email to