Automatic generation of changelog

7 views
Skip to first unread message

Arnar Birgisson

unread,
Mar 28, 2007, 5:39:09 PM3/28/07
to sqlal...@googlegroups.com
Hi there,

I have an old system that I'm porting over to SA. In the old system
there is one entity that keeps a change history of itself. In the code
that performs db updates, the current state of the object is examined
and before it is updated I insert rows to a table with colums like
this:

object id
change-event-id
fieldname
old-value
new-value

This is used to render a change history of the entity to the user.

Now, can I automate this somehow with SA? Preferably I'd like to do
this automatically on object update. Can I use the "dirty" set in the
session to find out what columns of the mapped table have changed?

How do I hook into the update action - I think I need a mapper
extension, but can you point me to some examples?

Has anyone done something similar (the whole changelog thing) with SA?

Arnar

Michael Bayer

unread,
Mar 28, 2007, 5:52:03 PM3/28/07
to sqlalchemy
dont have an example handy, but yeah youd want to make a
MapperExtension and work into the after_insert(), after_update() and
after_delete() hooks (or maybe the before_ versions of each one,
depending on how you detect changes). you can issue writes to the
database immediately within those and theyll be within the current
transaction.

theres an example of a "write" side MapperExtension in examples/pickle/
custom_pickler.py .

if you want some help on actually detecting "whats changed", you can
use some features of the attributes package to do so. if you have an
instance of MyClass and want to inspect the "history" of
instance.someattribute:

history = MyClass.someattribute.get_history(instance, passive=True)

"passive=True" means "dont fire off lazy loaders". that call returns
to you an AttributeHistory object, with which you can say:

history.is_modified()
history.added_items() -> returns a list of new values, a one-element
list for scalar attributes
history.deleted_items() -> returns a list of deleted values, a one -
element list for scalar attributes
history.unchanged_items() -> etc

note that by "history", we mean things that have occured since the
instance was loaded from the database into the current session.

Arnar Birgisson

unread,
Mar 28, 2007, 6:07:24 PM3/28/07
to sqlal...@googlegroups.com
Hi Dave,

Thank you very much, seems I should be able to do what I want. I'll
take a stab at it tomorrow and report.

On 3/28/07, Michael Bayer <zzz...@gmail.com> wrote:
> note that by "history", we mean things that have occured since the
> instance was loaded from the database into the current session.

That history is cleared on session.flush() - right?

Arnar

Michael Bayer

unread,
Mar 28, 2007, 7:48:34 PM3/28/07
to sqlal...@googlegroups.com

yes

Gaetan de Menten

unread,
Mar 29, 2007, 3:38:56 AM3/29/07
to sqlal...@googlegroups.com
On 3/29/07, Arnar Birgisson <arn...@gmail.com> wrote:
>
> Thank you very much, seems I should be able to do what I want. I'll
> take a stab at it tomorrow and report.

If you could post your code somewhere, it would be greatly appreciated
since I know I'll need to do approximately the same thing in a future
project of mine. And I guess I'm not the only one interested by such a
feature.

> On 3/28/07, Michael Bayer <zzz...@gmail.com> wrote:
> > note that by "history", we mean things that have occured since the
> > instance was loaded from the database into the current session.
>
> That history is cleared on session.flush() - right?
>
> Arnar
>

--
Gaëtan de Menten
http://openhex.org

imbunche

unread,
Mar 31, 2007, 8:52:49 PM3/31/07
to sqlalchemy
I'd also apreciate if you post your code later.

thx in advance.
IvO

Arnar Birgisson

unread,
Mar 31, 2007, 10:51:19 PM3/31/07
to sqlal...@googlegroups.com
Gaetan and Ivo:

Sure thing. I'll implement this one way or another monday or tuesday,
will post what I end up with.

Arnar

Arnar Birgisson

unread,
Apr 4, 2007, 11:45:03 AM4/4/07
to sqlal...@googlegroups.com
Hi Mike,

I've started looking into this and this is exactly what I need. I can
pretty much eat the stuff directly from added_items() and
deleted_items() and insert that into my history-log table.

One question though:

On 3/28/07, Michael Bayer <zzz...@gmail.com> wrote:
>

> dont have an example handy, but yeah youd want to make a
> MapperExtension and work into the after_insert(), after_update() and
> after_delete() hooks (or maybe the before_ versions of each one,
> depending on how you detect changes). you can issue writes to the
> database immediately within those and theyll be within the current
> transaction.

Can I use the ORM inside after_update? I.e. can I create new instances
of say HistoryLoggedItem which is a mapped class and expect that to be
inserted in the same transaction, or do I need to insert explicitly
into that table?

Arnar

Michael Bayer

unread,
Apr 4, 2007, 12:10:47 PM4/4/07
to sqlal...@googlegroups.com

On Apr 4, 2007, at 11:45 AM, Arnar Birgisson wrote:

>
> Can I use the ORM inside after_update? I.e. can I create new instances
> of say HistoryLoggedItem which is a mapped class and expect that to be
> inserted in the same transaction, or do I need to insert explicitly
> into that table?

its not going to be able to modify the current flush() plan thats in
progress, so youd either have to insert yourself, dispatch to
class_mapper(HistoryLoggedItem).save_obj() yourself, or process the
Session externally to the flush(), create the HLI items at that point
and add them (by perusing session.dirty and processing).

the third option is slightly less automatic but would be the more
official "OO" way to do it, being that SA is a library, not a
framework. when you try to add behavior by patching inside of SA's
flush process, youre starting to use it as a framework. Ive noticed
the Python community *really* wants to turn things into frameworks :).

Arnar Birgisson

unread,
Apr 4, 2007, 12:52:11 PM4/4/07
to sqlal...@googlegroups.com
On 4/4/07, Michael Bayer <mik...@zzzcomputing.com> wrote:
> its not going to be able to modify the current flush() plan thats in
> progress, so youd either have to insert yourself, dispatch to
> class_mapper(HistoryLoggedItem).save_obj() yourself, or process the
> Session externally to the flush(), create the HLI items at that point
> and add them (by perusing session.dirty and processing).

Ok, dispatching to class_mapper(...).save_obj() will get the sql
executed right away? Will I need to .refresh() the HLI instances
afterwards?

The third option, creating the changelog-information independent of
the flush - would that mean I can't use a mapper-extension to trigger
it? Do I have any other ways for triggering things on object update.

> the third option is slightly less automatic but would be the more
> official "OO" way to do it, being that SA is a library, not a
> framework. when you try to add behavior by patching inside of SA's
> flush process, youre starting to use it as a framework. Ive noticed
> the Python community *really* wants to turn things into frameworks :).

Well, dynamic and introspective languages tend to be "framework
friendly" I guess :o)

Does it hurt in anyway to use passive=False when I get_history? In
some cases I had to in order to get what I want (get_history was
returning None).

thanks,
Arnar

Arnar Birgisson

unread,
Apr 4, 2007, 12:56:12 PM4/4/07
to sqlal...@googlegroups.com
Hi again

On 4/4/07, Arnar Birgisson <arn...@gmail.com> wrote:
> The third option, creating the changelog-information independent of
> the flush - would that mean I can't use a mapper-extension to trigger
> it? Do I have any other ways for triggering things on object update.

I just realized that I need to do more than just generate the
HistoryItems. I need to create a HistoryLogEvent (which is parent to
the items) and specify a log message and other kinds of stuff - so I
will need to do that anyway. Of course I call the
historyitem-generator from there so I don't need to use a mapper
extension.

I'm going on a holiday until next week - I'll post the code when I
come back for those that are interested.

Arnar

Reply all
Reply to author
Forward
0 new messages