Whats a good general way to look SQLAlchemy transactions, complete with authenticated user, etc?

2024/10/3 14:31:39

I'm using SQLAlchemy's declarative extension. I'd like all changes to tables logs, including changes in many-to-many relationships (mapping tables). Each table should have a separate "log" table with a similar schema, but additional columns specifying when the change was made, who made the change, etc.

My programming model would be something like this:

row.foo = 1
row.log_version(username, change_description, ...)

Ideally, the system wouldn't allow the transaction to commit without row.log_version being called.

Thoughts?

Answer

There are too many questions in one, so they that full answers to all them won't fit StackOverflow answer format. I'll try to describe hints in short, so ask separate question for them if it's not enough.

Assigning user and description to transaction

The most popular way to do so is assigning user (and other info) to some global object (threading.local() in threaded application). This is very bad way, that causes hard to discover bugs.

A better way is assigning user to the session. This is OK when session is created for each web request (in fact, it's the best design for application with authentication anyway), since there is the only user using this session. But passing description this way is not as good.

And my favorite solution is to extent Session.commit() method to accept optional user (and probably other info) parameter and assign it current transaction. This is the most flexible, and it suites well to pass description too. Note that info is bound to single transaction and is passed in obvious way when transaction is closed.

Discovering changes

There is a sqlalchemy.org.attributes.instance_state(obj) contains all information you need. The most useful for you is probably state.committed_state dictionary which contains original state for changed fields (including many-to-many relations!). There is also state.get_history() method (or sqlalchemy.org.attributes.get_history() function) returning a history object with has_changes() method and added and deleted properties for new and old value respectively. In later case use state.manager.keys() (or state.manager.attributes) to get a list of all fields.

Automatically storing changes

SQLAlchemy supports mapper extension that can provide hooks before and after update, insert and delete. You need to provide your own extension with all before hooks (you can't use after since the state of objects is changed on flush). For declarative extension it's easy to write a subclass of DeclarativeMeta that adds a mapper extension for all your models. Note that you have to flush changes twice if you use mapped objects for log, since a unit of work doesn't account objects created in hooks.

https://en.xdnf.cn/q/70721.html

Related Q&A

OpenCV - Tilted camera and triangulation landmark for stereo vision

I am using a stereo system and so I am trying to get world coordinates of some points by triangulation.My cameras present an angle, the Z axis direction (direction of the depth) is not normal to my sur…

Node.jss python child script outputting on finish, not real time

I am new to node.js and socket.io and I am trying to write a small server that will update a webpage based on python output. Eventually this will be used for a temperature sensor so for now I have a du…

lambda function returning the key value for use in defaultdict

The function collections.defaultdict returns a default value that can be defined by a lambda function of my own making if the key is absent from my dictionary.Now, I wish my defaultdict to return the u…

Calling Matlab function from python

I have one project in which I have one one matlab code which I have to run tho Django. I tried installing Mlabwrap ..But it gives me following error.Traceback (most recent call last): File "<st…

Suds ignoring proxy setting

Im trying to use the salesforce-python-toolkit to make web services calls to the Salesforce API, however Im having trouble getting the client to go through a proxy. Since the toolkit is based on top of…

CSV to JSON script

I took this script from here: import csv from itertools import izip f = open( /django/sw2/wkw2/csvtest1.csv, r ) reader = csv.reader( f ) keys = ( "firm_url", "firm_name", "fir…

Accessing an ALREADY running process, with Python

Question: Is there a way, using Python, to access the stdout of a running process? This process has not been started by Python.Context: There is a program called mayabatch, that renders out images fro…

sum up two pandas dataframes with different indexes element by element

I have two pandas dataframes, say df1 and df2, of some size each but with different indexes and I would like to sum up the two dataframes element by element. I provide you an easy example to better und…

Urwid: make cursor invisible

Im using urwid, which is a Python "framework" for designing terminal user interfaces in ncurses. Theres one thing though that Im not able to do in urwid that was easy in curses - make the cur…

How do I use scipy.weave.inline together with external C libraries?

I am trying to understand weave.inline to wrap C code in my Python programs. The code below simply takes the Numpy array and multiplicates all of its elements by 2.inl.py import numpy import scipy.weav…