Hi, After some more internal discussions we tried and realized that the version col is not really required at the child table level. The second commit in the sample code fails because the version value on the parent row has already moved ahead.
Please confirm if the understanding/conclusion is correct. Regards, Naresh On Mon, Nov 29, 2010 at 2:21 PM, Naresh <nkhal...@vayana.in> wrote: > Hi, > > We have a parent class/table (Notification/notifications) and a child > class/table(BusinessNotification/business_notifications). > Both of them have a version_id_col defined. > We are facing ConcurrentModificationException errors for the model at > the end of the mail. > The problem is because the update state generated uses 'None' a the > value for notifications.version column. > > We tried removing the version_id_col mapper arg in the child class - > BusinessNotification and then the error went away and the update > statement used the correct version column value to update. > > So, firstly we are wondering how do we fix the situation that we can > have a version_id_col in the child class/table as well? > > Upon further investigations, we noticed that in the other parts of > the application we used the following approach. > > parent_table = Table( ... ) > class Parent(DeclarativeBase): > __table__ = parent_table > __mapper_args__ = {'polymorphic_on': parent_table.c.type, > 'version_id_col': parent_table.c.version} > > child1_table = Table( ... ) > class Child1(DeclarativeBase) > __table__ = child1_table > __mapper_args__ = {'polymorphic_identity':'c1', > 'version_id_col':child1_table.c.version_num} > > With the above style of coding our models, we don't get > ConcurrentModificationException, but any update statement to the > parent_table does not use version column. > > > Please throw some light on these behaviors. > > Regards, > Naresh > > P.S. The model and sample client code follows. > ------------------ > > class Notification(DeclarativeBase): > """This entity represents the notifications saved for sending > later """ > __tablename__ = 'notifications' > __table_args__ = {"mysql_engine":"InnoDB", > "mysql_row_format":"DYNAMIC", > "mysql_charset":"utf8"} > > id = Column(bigint, primary_key=True, autoincrement=False) > entid = Column(bigint, ForeignKey('enterprises.entid', > name='fk_notifn_entid'), nullable=False) > notification_type = Column(types.String(30), nullable=False) > source = Column(types.String(30), nullable=False) > data = Column(text_type, nullable=False) > status = Column(types.String(12), nullable=False, default='new') > created_on = Column(types.DATETIME, nullable=False, > default=func.current_timestamp()) > last_updated = Column(types.DATETIME, nullable=True, > onupdate=func.current_timestamp()) > version = Column(types.SMALLINT, nullable=False, default=0) > __mapper_args__ = {'polymorphic_on': notification_type, > 'polymorphic_identity':'incident', 'version_id_col': version} > > > class BusinessNotification(Notification): > """This entity represents the business notifications """ > __tablename__ = 'business_notifications' > __table_args__ = {"mysql_engine":"InnoDB", > "mysql_row_format":"DYNAMIC", > "mysql_charset":"utf8"} > > notification_id = Column(bigint, ForeignKey('notifications.id', > name='fk_bn_notificationid'), primary_key=True, autoincrement=False) > event_id = Column(bigint, ForeignKey('doc_events.id', > name='fk_bn_eventid'), nullable=True) > created_on = Column(types.DATETIME, nullable=False, > default=func.current_timestamp()) > last_updated = Column(types.DATETIME, nullable=True, > onupdate=func.current_timestamp()) > version_num = Column(types.SMALLINT, nullable=False, default=0) > __mapper_args__ = {'polymorphic_identity':'event', > 'version_id_col': version_num} > # __mapper_args__ = {'polymorphic_identity':'event'} > > > if __name__ == '__main__': > try : > qry = meta.Session().query(BusinessNotification).filter(and_( > > BusinessNotification.status=='new', > > BusinessNotification.created_on < now())) > qry = qry.order_by(BusinessNotification.created_on) > all_notifications = qry.all() > for notification in all_notifications : > # some business logic to trigger status update > notification.status='in_progress' > meta.Session().commit() > except Exception as ex : > log.error("Error Identifying Notification Recipients") > log.exception(ex) > meta.Session().rollback() > finally : > meta.Session.remove() > -- Naresh Khalasi | Vayana Services (http://www.vayana.in) | +91-9892294598 -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalch...@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.
from sqlalchemy import create_engine, MetaData, Column, types, func, ForeignKey, orm from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.dialects import mysql import datetime bigint = mysql.BIGINT(unsigned=True) dbengine = create_engine("mysql://testuser:test...@localhost/testdb", echo=True) Session = orm.scoped_session(orm.sessionmaker(bind=dbengine, autoflush=True, autocommit=False, expire_on_commit=True)) Session2 = orm.scoped_session(orm.sessionmaker(bind=dbengine, autoflush=True, autocommit=False, expire_on_commit=True)) # Global metadata. If you have multiple databases with overlapping table # names, you'll need a metadata for each database metadata = MetaData() metadata.bind = dbengine class Base(object): __table_args__ = {"mysql_engine":"InnoDB", "mysql_row_format":"DYNAMIC", "mysql_charset":"utf8"} def __repr__(self): L = [] for k in self.__class__.__table__.c.keys(): value = getattr(self, k, '') L.append("%s=%r" % (k, value)) return '%s(%s)' % (self.__class__.__name__, ','.join(L)) # Assign the same metadata object we created earlier DeclarativeBase = declarative_base(cls=Base, metadata=metadata) class Parent(DeclarativeBase): __tablename__ = 'parents' id = Column(bigint, primary_key=True) otype = Column(types.CHAR(10), nullable=False) status = Column(types.CHAR(20), nullable=False) created_on = Column(types.DATETIME, nullable=False, default=func.current_timestamp()) last_updated = Column(types.DATETIME, nullable=True, onupdate=func.current_timestamp()) version = Column(types.SMALLINT, nullable=False, default=0) __mapper_args__ = {'polymorphic_on': otype, 'polymorphic_identity':'parent', 'version_id_col': version} class Child1(Parent): __tablename__ = 'c1' cid = Column(bigint, ForeignKey('parents.id', name='fk_c1parents_id'), primary_key=True, autoincrement=False) desc = Column(types.Unicode(100), nullable=True) __mapper_args__ = {'polymorphic_identity':'c1'} class Child2(Parent): __tablename__ = 'c2' cid = Column(bigint, ForeignKey('parents.id', name='fk_c2parents_id'), primary_key=True, autoincrement=False) desc = Column(types.Unicode(100), nullable=True) __mapper_args__ = {'polymorphic_identity':'c2'} print '--------------------------------' metadata.drop_all() print '--------------------------------' metadata.create_all() print '--------------------------------' if __name__ == '__main__': dbsession = Session() c11 = Child1() c11.status = 'new' dbsession.add(c11) c12 = Child1() c12.status = 'new' dbsession.add(c12) c21 = Child2() c21.status = 'new' dbsession.add(c21) dbsession.commit() Session.remove() dbsession = Session() dbs2 = Session2() query = dbsession.query(Child1) all_c1 = query.all() for c1 in all_c1: c1.desc = 'Update at %s' %(datetime.datetime.now()) query2 = dbs2.query(Child1) all_c12 = query2.all() for c13 in all_c12: c13.desc = 'Update2 at %s' %(datetime.datetime.now()) print "============================= Initiating Commit 1 ===================================" dbsession.commit() print "============================= Completed Commit 1 ===================================" print "============================= Initiating Commit 2 ===================================" dbs2.commit() print "============================= Completed Commit 1 ==================================="