Default scope of variables

Frank Millman frank at chagford.com
Tue Jul 9 03:35:34 EDT 2013


"Chris Angelico" <rosuav at gmail.com> wrote in message 
news:CAPTjJmqkmFd4-Jpugr-vubuB6riBV6K_Mwnxc_U3CVaBr_Wgbg at mail.gmail.com...
> On Tue, Jul 9, 2013 at 4:08 PM, alex23 <wuwei23 at gmail.com> wrote:
>> On 9/07/2013 3:07 PM, Chris Angelico wrote:
>>>
>>> The subtransactions are NOT concepted as separate transactions. They
>>> are effectively the database equivalent of a try/except block.
>>
>>
>> Sorry, I assumed each nested query was somehow related to the prior
>> one. In which case, I'd probably go with Ethan's suggestion of a
>> top-level transaction context manager with its own substransaction
>> method.
>
> Yeah, that would probably be the best option in this particular
> instance. Though I do still like the ability to have variables shadow
> each other, even if there's a way around one particular piece of code
> that uses the technique.
>

I have been following this sub-thread with interest, as it resonates with 
what I am doing in my project.

In my case, one update can trigger another, which can trigger another, etc. 
It is important that they are treated as a single transaction. Each object 
has its own 'save' method, so there is not one place where all updates are 
executed, and I found it tricky to control.

I came up with the following context manager -

class DbSession:
    """
    A context manager to handle database activity.
    """

    def __init__(self):
        self.conn = None
        self.no_connections = 0
        self.transaction_active = False

    def __enter__(self):
        if self.conn is None:
            self.conn = _get_connection()  # get connection from pool
            self.conn.cur = self.conn.cursor()
            # all updates in same transaction use same timestamp
            self.conn.timestamp = datetime.now()
        self.no_connections += 1
        return self.conn

    def __exit__(self, type, exc, tb):
        if type is not None:  # an exception occurred
            if self.transaction_active:
                self.conn.rollback()
                self.transaction_active = False
            self.conn.release()  # return connection to pool
            self.conn = None
            return  # will reraise exception
        self.no_connections -= 1
        if not self.no_connections:
            if self.transaction_active:
                self.conn.commit()
                self.transaction_active = False
            self.conn.cur.close()
            self.conn.release()  # return connection to pool
            self.conn = None

All objects created within a session share a common DbSession instance.

When any of them need any database access, whether for reading or for 
updating, they execute the following -

    with db_session as conn:
        conn.transaction_active = True  # this line must be added if 
updating
        conn.cur.execute(__whatever__)

Now it 'just works'. I don't have the need for save-points - either all 
updates happen, or none of them do.

Frank Millman






More information about the Python-list mailing list