My use of web2py requires an audit trail for (essentially) all database 
tables; The preferable way to do that is to keep a "foo_history" table for 
each table "foo". The records (both audited and original) need to have a 
"revision" that increments with each update, and "last modified on", "last 
modified by" fields that get updated with the user_id and the time on each 
update.

At this point in time, there is no need to delete records - but if there 
was, I'm not sure what the proper way would be -- possibly having another 
field "deleted" on the audit trail table, which would be set to "true" on 
the revision that was the latest version.

Now, the built in Crud system gives me most of what I want (I don't see how 
to make it keep the history of the deleted table above, though for now 
that's not a problem). But there's quite a bit of application logic that 
needs this which cannot be implemented using the Crud.

The "classic" solution is to write this as stored procedures. I would like 
to avoid that for portability and maintenance reasons. Another solution is 
to use my own "update/insert/delete" methods that update audit tables 
whenever a record updates. That's error prone, and I would rather avoid 
that, especially since I _do_ want to build on web2py infrastructure such as 
Crud for other things.

The solution that I'm thinking of is to hook the DAL; specifically, the 
following functions would be sufficient for me; It seems that all database 
modifications go through them eventually, whether called directly or when 
manipulating Rows or Row objects.

insert: (copy all newly added records to the audit table table - easy)
update: (copy all modified records to the audit table, add a 'revision = 
revision + 1' to the SET code - easy)
delete: (copy all deleted records to the audit table, adding a 'deleted = 
True' - possibly a little more complicated)

For completeness, hooking "truncate" and "drop table" to do the same thing 
(either raise an error if that is disallowed, or transform it into a delete 
of each individual record so that everything goes into the audit). And I 
would also disable migrations if I do that, because they may change the 
database without updating my audit trail.

Now, before I start working on a monkey patch (or full patch) to dal.py - 
perhaps I've missed a simpler way to do the same thing?

If I haven't - I'm currently working with Postgres and SQLite; I would like 
to contribute the resulting patch to the web2py project, but have no time to 
evaluate and test on other databases/gae at the moment. Is there anything 
else I should be aware of that would be required for other databases?
 
====

Finally, a question about transaction rollback - in my app, I sometimes want 
to rollback everything (as if I did e.g. raise 
MySecurityException("Forbidden")), but actually return a non-error HTML 
result to the user. I am aware of db.rollback(), but the exception handling 
code does more, e.g., today it does:

 if response._custom_rollback:
   response._custom_rollback()
 else:
   BaseAdapter.close_all_instances('rollback')

I would suggest adding an "response._transaction_verb" variable, which would 
default to 'COMMIT', but could be changed to 'ROLLBACK', and would determine 
what to do with the database (commit/rollback) in the case of a successful 
completion - meaning raising of an HTTP exception or simple return from 
controller.

On other exception, I would expect rollback always (and the application code 
can call db.commit or BaseAdapter.close_all_instances('commit') or whatever 
if needed) - because committing on error is something that has to be 
extremely explicit.

What say you?

Reply via email to