views:

102

answers:

3

Hi,

I have a Python function which receives numerous variables, and builds an SQL query out of them:

def myfunc(name=None, abbr=None, grade=None, ...)

These values should build an SQL query. For that purpose, Those who equal None should be changed to NULL, and those who store useful values should be embraced with 's:

name="'"+name+"\'" if name else 'NULL'
abbr="'"+abbr+"\'" if abbr else 'NULL'
...
Lots of lines here - that's my problem!
...

And than,

query="""INSERT INTO table(name, abbr, ...)
         VALUES (%(name)s, %(abbr)s, ...) """ locals()
cur.execute(query)

Is there a nicer, more Pythonic way to change the variable contents according to this rule?

Adam

+5  A: 

The best way to form a SQL query is not by string-formatting -- the execute method of a cursor object takes a query string with placeholders and a sequence (or dict, depending on the exact implementation you have of the DB API) with the values to substitute there; it will then perform the None-to-Null and string-quoting that you require.

I strongly recommend you look into that possibility. If you need string processing for some other purpose, however, you could do something like:

processed = dict((n, "'%s'" % v if v is not None else 'NULL')
                 for n, v in locals().iteritems())

and then use dictionary processed instead of locals() for further string-formatting.

Alex Martelli
+1 Learned something today, thanks.
Adam Matan
@Adam, you're most welcome!
Alex Martelli
Do you have a link for some materials regarding formatting a query with placeholders?
Adam Matan
@Adam, for example, for the sqlite that's bundled with Python in the standard library, see http://docs.python.org/library/sqlite3.html?highlight=sqlite#sqlite3.Cursor.execute -- other DB API adapters use different markers than '?', e.g. '%s' in MySQLDB, etc, etc.
Alex Martelli
Great, thanks, I found it in my search and wondered if its relevant.
Adam Matan
@Adam Matan: Relevant doesn't begin to describe how central it is. It's the only sane way to bind values into SQL statements.
S.Lott
A: 

You could define myfunc as follows:

def myfunc(*args, **kwargs)

Where kwargs is a dictionary holding all named parameters passed to the function.

To get the value of a query parameter, you would use kwargs.get(name_of_parameter, 'NULL'). To build the query, you would just iterate over all dictionary items. Note however, that any parameter passed as a named parameter to the function will end up in the query if you do it this way.

sttwister
It is an option, bu it seriously deteriorates the readability of my code.
Adam Matan
A: 

The correct way to pass arguments to psycopg2 is to use placeholders and let the driver handle the values. None are converted to NULL automatically and the correct string escaping is performed.

Concatenating string is a bad idea.

piro