Hi Michael, Thank you very much for your fast response. I will give it a try.
I tried to remove unneeded stuff but I think that the problem is in the track_changes mixin as I never got this error before using it. If you need more stuff or do find something, I'd be happy to know. BTW: for initialization, I use: DB_URL="oracle://user:pass@url/db" engine = create_engine(DB_URL, echo=False, pool_recycle=3600) Session = scoped_session(sessionmaker(bind=engine, expire_on_commit=False)) session = tracked_session(Session()) Base = declarative_base() Cheers, Ofir On Thursday, April 30, 2015 at 7:03:10 PM UTC+3, Michael Bayer wrote: > > > > On 4/30/15 11:00 AM, Ofir Herzas wrote: > > Hi, > I'm using sqlalchemy 0.9.7 and cx_oracle 5.1.3 and every once in a while > (inconsistent), I get the following error: > > Traceback (most recent call last): > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > > line 1919, in flush > self._flush(objects) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > > line 2037, in _flush > transaction.rollback(_capture_exception=True) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", > > line 60, in __exit__ > compat.reraise(exc_type, exc_value, exc_tb) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > > line 2001, in _flush > flush_context.execute() > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", > > line 372, in execute > rec.execute(self) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", > > line 526, in execute > uow > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", > > line 65, in save_obj > mapper, table, insert) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", > > line 602, in _emit_insert_statements > execute(statement, params) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/base.py", > > line 729, in execute > return meth(self, multiparams, params) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", > > line 321, in _execute_on_connection > return connection._execute_clauseelement(self, multiparams, params) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/base.py", > > line 826, in _execute_clauseelement > compiled_sql, distilled_params > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/base.py", > > line 978, in _execute_context > context._fetch_implicit_returning(result) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/default.py", > > line 815, in _fetch_implicit_returning > ipk.append(row[c]) > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/result.py", > > line 331, in _key_fallback > expression._string_or_unprintable(key)) > NoSuchColumnError: "Could not locate column in row for column ' > t_shift_employee_change.id'" > [30/Apr/2015:15:55:17] EXCEPTION > # ---- EXCEPTION DESCRIPTION BEGIN ---- # > # ---- Type ---- # > NoSuchColumnError > # ---- Detail ---- # > "Could not locate column in row for column 't_shift_employee_change.id'" > # ---- Traceback ---- # > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > > line 1919, in flush > self._flush(objects) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > > line 2037, in _flush > transaction.rollback(_capture_exception=True) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", > > line 60, in __exit__ > compat.reraise(exc_type, exc_value, exc_tb) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > > line 2001, in _flush > flush_context.execute() > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", > > line 372, in execute > rec.execute(self) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", > > line 526, in execute > uow > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", > > line 65, in save_obj > mapper, table, insert) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", > > line 602, in _emit_insert_statements > execute(statement, params) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/base.py", > > line 729, in execute > return meth(self, multiparams, params) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", > > line 321, in _execute_on_connection > return connection._execute_clauseelement(self, multiparams, params) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/base.py", > > line 826, in _execute_clauseelement > compiled_sql, distilled_params > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/base.py", > > line 978, in _execute_context > context._fetch_implicit_returning(result) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/default.py", > > line 815, in _fetch_implicit_returning > ipk.append(row[c]) > - > File > "/opt/enigmai/ve/python-2.7.6/lib/python2.7/site-packages/sqlalchemy/engine/result.py", > > line 331, in _key_fallback > expression._string_or_unprintable(key)) > # ---- EXCEPTION DESCRIPTION END ---- # > > I saw several posts about this issue but I didn't find a solution. > Restarting my service seems to fix the issue for a while. > > BTW: This error is linked to a mixin class I've created for > track-changes based on the version mixin from > > * > http://docs.sqlalchemy.org/en/latest/_modules/examples/versioned_history/history_meta.html > > <http://docs.sqlalchemy.org/en/latest/_modules/examples/versioned_history/history_meta.html> > > * > > > this error refers to a wide variety of situations where the ORM is running > a query that is failing to compose correctly. It's a bug in SQLAlchemy. > That you are getting this within an INSERT statement from the RETURNING > clause in oracle strongly suggests this is a bug in the Oracle dialect. > If you can post minimal mappings and the stack trace here as a bug report > that would be very helpful. > > Note also that the Oracle dialect doesn't do very well with composite > primary keys in conjunction with RETURNING. There may have been some > limitations I came across in my testing here, so there is a note at > http://docs.sqlalchemy.org/en/rel_1_0/dialects/oracle.html#returning-support > regarding this. While I still want to know how you're getting exactly > this kind of error since it would be nice to anticipate it better, the > section here illustrates the implicit_returnining=False flag that would > resolve these errors you're getting. > > > > -- > 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+...@googlegroups.com <javascript:>. > To post to this group, send email to sqlal...@googlegroups.com > <javascript:>. > Visit this group at http://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout. > > > -- 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 http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
from track_changes import Tracked import sqlalchemy as sa def before_shift_employee_change_commit(cls, deleted): if deleted: cls.field4 = False class ShiftEmployee(Base, Tracked): __tablename__ = "t_shift_employee" __table_args__ = (sa.UniqueConstraint('field1', 'field2', 'field3'),) __track__ = { 'keep': ['field1', 'field2', 'field3'], 'cols': ['field4'], 'before_commit': before_shift_employee_change_commit } id = sa.Column(sa.BigInteger, sa.Sequence('%s_id_seq' % __tablename__), nullable=False, primary_key=True) field1 = sa.Column(sa.BigInteger, nullable=False) field2 = sa.Column(sa.BigInteger, nullable=False) field3 = sa.Column(sa.Date, nullable=False) field4 = sa.Column(sa.Boolean, nullable=False) @classmethod def get(cls, field1=None, field3=None, field2=None, **kwargs): try: field3 = field3.date() except: pass kwargs = {k:v for k,v in kwargs.items() if hasattr(ShiftEmployee, k)} try: item = cls._get_from_db(field1, field3, field2) updated = False except sa.orm.exc.NoResultFound: try: # create and add a new item updated = True item = cls(field1=field1, field3=field3, field2=field2, **kwargs) session.add(item) session.flush() except sa.exc.IntegrityError: session.rollback() try: item = cls._get_from_db(field1, field3, field2) except: raise except: # something else happened session.rollback() raise if not updated: for k, v in kwargs.items(): if getattr(item, k) != v: setattr(item, k, v) return item @classmethod def _get_from_db(cls, field1=None, field3=None, field2=None): return session.query(cls).\ filter(cls.field1 == field1).\ filter(cls.field3 == field3).\ filter(cls.field2 == field2).\ one()
""" Tracked mixin class and other utilities. Generated from the Version mixin (http://docs.sqlalchemy.org/en/latest/_modules/examples/versioned_history/history_meta.html) """ from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import mapper, attributes, object_mapper from sqlalchemy.orm.exc import UnmappedColumnError from sqlalchemy.orm.properties import RelationshipProperty from sqlalchemy import Table, Column, ForeignKeyConstraint, Integer, DateTime, BigInteger, SmallInteger, Sequence from sqlalchemy import event, util import datetime def col_references_table(col, table): for fk in col.foreign_keys: if fk.references(table): return True return False def _is_tracked_col(col, table, type='cols'): try: tracked_cols = table.__track__[type] except: tracked_cols = [] return "track_meta" not in col.info and col.key in tracked_cols def _history_mapper(local_mapper): cls = local_mapper.class_ # set the "active_history" flag # on column-mapped attributes so that the old version # of the info is always loaded (currently sets it on all attributes) for prop in local_mapper.iterate_properties: getattr(local_mapper.class_, prop.key).impl.active_history = True super_mapper = local_mapper.inherits super_history_mapper = getattr(cls, '__history_mapper__', None) polymorphic_on = None super_fks = [] def _col_copy(col): orig = col col = col.copy() orig.info['history_copy'] = col col.unique = False col.default = col.server_default = None return col properties = util.OrderedDict() if not super_mapper or local_mapper.local_table is not super_mapper.local_table: cols = [] track_meta = {"track_meta": True} # add column.info to identify columns specific to tracked changes for column in local_mapper.local_table.c: if not _is_tracked_col(column, cls) and not _is_tracked_col(column, cls, 'keep'): continue col = _col_copy(column) if super_mapper and col_references_table(column, super_mapper.local_table): super_fks.append( (col.key, list(super_history_mapper.local_table.primary_key)[0]) ) cols.append(col) if column is local_mapper.polymorphic_on: polymorphic_on = col orig_prop = local_mapper.get_property_by_column(column) # carry over column re-mappings if len(orig_prop.columns) > 1 or orig_prop.columns[0].key != orig_prop.key: properties[orig_prop.key] = tuple(col.info['history_copy'] for col in orig_prop.columns) if super_mapper and 'changed_by_fk' in cls.__track__: try: super_fks.append(('changed_by', cls.__track__['changed_by_fk'])) except: pass __tablename__ = local_mapper.local_table.name + '_change' # "id" stores a unique change ID. This column is required. cols.insert(0, Column('id', BigInteger, Sequence('%s_id_seq' % __tablename__), primary_key=True, info=track_meta)) # "changed_at" column stores the UTC timestamp of when the # change was made. # This column is optional and can be omitted. cols.append(Column('changed_at', DateTime, default=datetime.datetime.now, info=track_meta)) # "changed_by" column stores the user id who # was responsible for this change row. # This column is optional and can be omitted. cols.append(Column('changed_by', BigInteger, info=track_meta)) if super_fks: cols.append(ForeignKeyConstraint(*zip(*super_fks))) table = Table( __tablename__, local_mapper.local_table.metadata, *cols, schema=local_mapper.local_table.schema ) else: # single table inheritance. take any additional columns that may have # been added and add them to the history table. for column in local_mapper.local_table.c: if column.key not in super_history_mapper.local_table.c: col = _col_copy(column) super_history_mapper.local_table.append_column(col) table = None if super_history_mapper: bases = (super_history_mapper.class_,) if table is not None: properties['changed_at'] = ( (table.c.changed_at, ) + tuple(super_history_mapper.attrs.changed_at.columns) ) else: bases = local_mapper.base_mapper.class_.__bases__ tracked_cls = type.__new__(type, "%sChange" % cls.__name__, bases, {}) m = mapper( tracked_cls, table, inherits=super_history_mapper, polymorphic_on=polymorphic_on, polymorphic_identity=local_mapper.polymorphic_identity, properties=properties ) cls.__history_mapper__ = m class Tracked(object): @declared_attr def __mapper_cls__(cls): def map(cls, *arg, **kw): mp = mapper(cls, *arg, **kw) _history_mapper(mp) return mp return map def tracked_objects(iter): for obj in iter: if hasattr(obj, '__history_mapper__'): yield obj def track_change(obj, session, user_id, deleted=False): obj_mapper = object_mapper(obj) history_mapper = obj.__history_mapper__ history_cls = history_mapper.class_ attr = {} obj_changed = False for om, hm in zip(obj_mapper.iterate_to_root(), history_mapper.iterate_to_root()): if hm.single: continue for hist_col in hm.local_table.c: track = _is_tracked_col(hist_col, obj) if not track and not _is_tracked_col(hist_col, obj, 'keep'): continue obj_col = om.local_table.c[hist_col.key] # get the value of the # attribute based on the MapperProperty related to the # mapped column. this will allow usage of MapperProperties # that have a different keyname than that of the mapped column. try: prop = obj_mapper.get_property_by_column(obj_col) except UnmappedColumnError: # in the case of single table inheritance, there may be # columns on the mapped table intended for the subclass only. # the "unmapped" status of the subclass column on the # base class is a feature of the declarative module. continue # expired object attributes and also deferred cols might not # be in the dict. force it to load no matter what by # using getattr(). old_value = getattr(obj, prop.key) try: default_value = obj_col.default.arg except: default_value = None # A 3-tuple of added, unchanged and deleted values a, u, d = attributes.get_history(obj, prop.key) attr[hist_col.key] = old_value if track: if (a and default_value != a[0]) or (d and default_value != d[0]) or (u and old_value != u[0]): obj_changed = True if not obj_changed: # not changed, but we have relationships. OK # check those too for prop in obj_mapper.iterate_properties: if isinstance(prop, RelationshipProperty) and \ attributes.get_history( obj, prop.key, passive=attributes.PASSIVE_NO_INITIALIZE).has_changes(): for p in prop.local_columns: if p.foreign_keys: obj_changed = True break if obj_changed is True: break if not obj_changed: return attr['changed_by'] = user_id hist = history_cls() for key, value in attr.items(): setattr(hist, key, value) try: obj.__track__['before_commit'](hist, deleted) except: raise session.add(hist) def tracked_session(session): @event.listens_for(session, 'before_flush') def before_flush(session, flush_context, instances): user_id = getattr(session, 'user_id', None) for obj in tracked_objects(session.new): track_change(obj, session, user_id) for obj in tracked_objects(session.dirty): track_change(obj, session, user_id) for obj in tracked_objects(session.deleted): track_change(obj, session, user_id, deleted=True) return session