I used a mapper extension.
On before_update, It will copy the record and add it to an array.
On after_update, It will store the new record which is an exact copy
of the old record before the update.

Please note, I implemented a base class for my "storage" units this is
the sqlaorm thing.
It will have the methods that you won't find in SQLA - Their more for
connivence for me and mostly 1 liner things like, return an instance
of the mapped class or the mapped class.

I also considered playing with the state in the instance_manager for
the instance of the mapped class but didn't try that.
I think someone said you can call a SQLA function to change the state
- or i might be imagining it.

Good luck, Have fun :-)

class SchemaObjectMapperExtension(MapperExtension):
        def __init__(self, sqlaorm, storage_manager):
            self._sqlaorm = sqlaorm

            self._revisions_storage =
storage_manager.get_revisions_storage()

            self._historical_add_in_progress = False
            self._historical_records_to_add = []

        def before_insert(self, mapper, connection, record):
            return sqlalchemy.orm.EXT_CONTINUE

        def before_delete(self, mapper, connection, record):
            return sqlalchemy.orm.EXT_CONTINUE

        def after_inserte(self, mapper, connection, instance):
            return sqlalchemy.orm.EXT_CONTINUE

        def after_delete(self, mapper, connection, instance):
            return sqlalchemy.orm.EXT_CONTINUE

        def before_update(self, mapper, connection, record):
            # Manage in progress flag
            if self._historical_add_in_progress:
                return sqlalchemy.orm.EXT_CONTINUE

            session_ = self._sqlaorm.get_session()
            if not session_.is_modified(record,
include_collections=False):
                return


            next_revision_id =
self._revisions_storage.get_next_revision()
            record.revision = next_revision_id

            # Save a historical version of the record
            Record = self._sqlaorm.get_orm_class()
            historical_session = self._sqlaorm._get_new_session()
            current_record = historical_session.query(Record).filter_by
(id=record.id).one()
            historical_record = self._sqlaorm.get_new_record()
            current_record.copy(historical_record)
            historical_record.id = None
            historical_session.close()
            self._historical_records_to_add.append(historical_record)

            if self._sqlaorm.is_schema_object_attribute():
                revisions_record.comment = ('ATTRIBUTE EDITED obect_id
= ' + str(record.object_id) + '\nrevision = ' + str(record.revision))
            else:
                revisions_record.comment = ('EDIT obect_id = ' + str
(record.object_id) + '\nrevision = ' + str(record.revision))

            self._revisions_storage.store_new_record(revisions_record)


            # continue
            return sqlalchemy.orm.EXT_CONTINUE

        def after_update(self, mapper, connection, instance):
            self._add_historical_records(mapper, connection, instance)

            # continue
            return sqlalchemy.orm.EXT_CONTINUE

        def _add_historical_records(self, mapper, connection, record):
            if self._historical_add_in_progress:
                return sqlalchemy.orm.EXT_CONTINUE

            self._historical_add_in_progress = True

            historical_session = self._sqlaorm._get_new_session()
            for historical_record in self._historical_records_to_add:
                historical_session.add(historical_record)
            historical_session.flush(self._historical_records_to_add)
            historical_session.close()

            self._historical_records_to_add = []

            # Reset in progress flag
            self._historical_add_in_progress = False



On Aug 25, 4:39 am, Martijn Faassen <faas...@startifact.com> wrote:
> Hi there,
>
> I'm looking into a reliable way to clone ORM-mapped instances, so that
> the clone, with some modifications, can be re-added to the database. I
> saw the following thread:
>
> http://groups.google.com/group/sqlalchemy/browse_thread/thread/6e162f...
>
> Unfortunately the solutions presented in this thread require the
> instantation of the class without arguments, and this is not something
> that works with arbitrary classes as an __init__ may have required
> arguments.
>
> I went with a solution that instead clones the record on the table
> level, going outside the ORM. I'd be nice though if there were an easy
> way to do this with the ORM. Would this be something that be sensible to
> add to SQLAlchemy itself? What would the implementation strategy be?
>
> Of course once one starts thinking about relations and the like, cloning
> can get pretty hairy. Perhaps for that reason the cloning of an instance
> doesn't have a general implementation in SQLAlchemy. You'd need to clone
> everything that relates to it as well, and that can get pretty involved.
> (I'd need such a thing though, as my use case involves items going
> through a workflow. each of the indirect "deep-copy" clones would
> require a modification as well)
>
> Regards,
>
> Martijn
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to