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
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.
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
yes
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
thx in advance.
IvO
Sure thing. I'll implement this one way or another monday or tuesday,
will post what I end up with.
Arnar
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
>
> 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 :).
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
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