views:

1373

answers:

6

I'm using sqlite3 in python 2.5. I've created a table that looks like this:

   create table votes (
      bill text,
      senator_id text,
      vote text)

I'm accessing it with something like this:

v_cur.execute("select * from votes")
row = v_cur.fetchone()
bill = row[0]
senator_id = row[1]
vote = row[2]

What I'd like to be able to do is have fetchone (or some other method) return a dictionary, rather than a list, so that I can refer to the field by name rather than position. For example:

bill = row['bill'] 
senator_id = row['senator_id']
vote = row['vote']

I know you can do this with MySQL, but does anyone know how to do it with SQLite?

Thanks!!!

+2  A: 

Sure, make yourself a DictConnection and DictCursor as explained and shown at http://oss.itsystementwicklung.de/trac/pysqlite/wiki/PysqliteFactories for example.

Alex Martelli
wouldn't this be preferred over the accepted solution, which looks dirty?
Cawas
@Cawas, I agree, but the original asker doesn't seem to be around so there's no way to change what _he_ chose to accept a year ago!-)
Alex Martelli
yeah, I know that's how SO works... I just wanted to confirm if it really would be preferred. ;-)
Cawas
A: 

You could do this:

fields = ['bill', 'senator_id', 'vote']
v_cur.execute('select %s from votes' % ', '.join(fields))
row = v_cur.fetchone()
row_dict = {}
for i in range(row):
    row_dict[fields[i]] = row[i]
jcoon
-1: Dynamically built SQL. Better to use v_cur.description and find the column names that way.
S.Lott
the fields variable is hard-coded...it's not the same as a typical dynamic SQL case, is it? I don't see how this could be bad.
jcoon
All generation of SQL statements through string manipulation are bad. (1) they obscure the statement, (2) it's one step away from SQL injection attacks, and (3) it can baffle the DB's optimizer. In this one instance, it not be totally evil. But-- in general -- it's bad.
S.Lott
+3  A: 

The way I've done this in the past:

def dict_factory(cursor, row):
    d = {}
    for idx,col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

Then you set it up in your connection:

from pysqlite2 import dbapi2 as sqlite
conn = sqlite.connect(...)
conn.row_factory = dict_factory

This works under pysqlite-2.4.1 and python 2.5.4.

Colin Burnett
This works great as a quick and dirty solution -- thanks!
Not even quick and dirty, the row_factory hook would be the sanest way to do this per-connection. Mind you, at least the builtin sqlite3 module already comes with a suitable row factory: sqlite3.Row [ http://docs.python.org/library/sqlite3.html#row-objects ]
Sii
+1  A: 

I know you're not asking this, but why not just use sqlalchemy to build an orm for the database? then you can do things like,


entry = model.Session.query(model.Votes).first()
print entry.bill, entry.senator_id, entry.vote

as an added bonus your code will be easily portable to an alternative database, and connections and whatnot will be managed for free.

blackkettle
+2  A: 

There is actually an option for this in sqlite3. Change the row_factory member of the connection object to sqlite3.Row:

conn = sqlite3.connect('db', row_factory=sqlite3.Row)

or

conn.row_factory = sqlite3.Row

This will allow you to access row elements by name--dictionary-style--or by index. This is much more efficient than creating your own work-around.

Eric
A: 

Another general approach is to simply persist any Python object with the data structure which one requires. Take a look at the y_serial Python module: http://yserial.sourceforge.net

Hope this helps your project... it uses SQLite and should be fast and simple enough to implement in 10 minutes.

code43