HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Simple decorator for creating and managing a database cursor

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
simplemanagingcreatingdatabaseforanddecoratorcursor

Problem

I'm working on a simple decorator that creates and manages a database cursor. The goal is to 'inject' the cursor as the first argument of the function, but not expose it to the caller.

Usage:

import psycopg2

@needsNewCursor
def findItem(cursor, item_id):
    cursor.execute('SELECT name FROM items WHERE id = %s', [item_id])
    item_result = cursor.fetchone()
    # ...
    return item

# caller only needs to know about the ID
findItem(1234)


Decorator:

def needsNewCursor(func):
    def decorator(*args, **kargs):
        print 'Creating new cursor'
        # This is slightly different in the final code, but I'm only
        # concerned about how I'm dealing with decorators, at the moment
        cursor = createDefaultCursor()
        if cursor:
            with cursor:
                # Pass the cursor in as the 1st param
                return func(cursor, *args, **kargs)

        # Give the function a chance to deal with fails
        return func(None, *args, **kargs)

    # QUESTION: Is this enough?
    decorator.__name__ = func.__name__
    return decorator


Is this a good approach?

Solution

No, I don't think this is a good approach. Anyone who sees the declaration def findItem(cursor, item_id): will call the function with two arguments and scratch one's head to no end when it does not work.

You could just as well have cursor = createDefaultCursor() as the first line of each function that uses a cursor. The number of lines does not increase as you can omit the @needsNewCursor line.

Even better might be to create a context manager to deal with opening and closing a cursor and start each function with a statement like with new_cursor() as cursor:. A context manager also has an opportunity to deal with exceptions raised in the with block.

Anyway, when writing decorators, instead of assigning yourself decorator.__name__ = func.__name__ you can apply the functools.wraps decorator that also copies the docstring.

One more thing: in your example needsNewCursor is a decorator. You should choose a different name for the inner function.

Context

StackExchange Code Review Q#33678, answer score: 5

Revisions (0)

No revisions yet.