[Tutor] overview of how data is handled [getting dictionaries from sql cursor / generators] (fwd)

Roeland Rengelink roeland.rengelink at chello.nl
Thu May 20 05:46:19 EDT 2004

Danny Yoo wrote:

>[Forwarding to tutor at python.org: please keep tutor at python.org in CC.  It
>gives other folks the opportunity to help answer your questions.]
<snipped forwarded message, answering to reply

>my code
>for count in range(0,len(myfields)):
>   mylist.append(myfields[count][0])
>for count in range(0,len(mydata[0])):
>    myrowdata.append(mydata[0][count] )
>d = dict(zip(mylist,myrowdata))
>The above works with only one record at a time.
>I'm using PyPgSQL and the resultset is a list.
> Because the of books I'm reading (O'Rielly) I was expecting a tuple but
>I got a list.   OK but now the routines in the books suggest that I use
>a dict.  So I figured out how to convert the data (mydata in this case)
>into a dict.  But then I realized that the two loops I was going to use
>(to convert the list into a dict) will take time.  I did not test how
>long.  But I also realized that converting has to be wrong.  The
>returned data must be used as a list - not converted  and then used.
>But  it appears that's what you are doing in the "getDictsFromCursor"  -
>converting into a dict ( I know to show me).
It's probably a good idea to stress again that the (list of) objects 
by cursor.fetchone() (cursor.fetchmany())  behave like a dict. It is 
a tuple, nor a list or dict. (It's a PgResultSet object). However, in the
context of what you're trying to do, these object are read-only. That 
is, you
can't change them, and even if you transform them into a list (or dict) and
change the values, there is no way these values are going to be 
updated in the the database.

You will need additional code for that (see below).

>In the java code you are looping thru the resultset.  That's all I want
>to do - at first.  Then I'll need some way to loop thru the data based
>on the field name.  Again it looks like I have to create the dict for
>each record.  This just can't be right.  Fetchone 1000's of times is
>also got to be wrong.   In the VFP world we have a "SCAN" that loops
>thru the resultset.  I can access the data by field name in VFP. How
>does one do same thing - loop thru the resultset (could be 1000's) and
>access the fields by their field name?
If the backend of VFP is an sql database (I wouldn't know) you can 
assume that
VFP is doing something like what I show below under the covers.

>One nice thing about getting a list is I can change the data in the list
>(can't in a tuple).  so how about something like:
>replace all extPrice with Qty * price (this is a very simple thing to do
>in VFP,  Delphi or Java.) in the resultset. Then open a connection and
>save the changed data.
You're not going to be able to do this, even after you have changed the 
set from as 'tuple' into a list, to make it editable. The reason that 
this is
not trivial, is that the result set does not know from which row in the
database it came, so it's not obvious which row in the database has to be
updated once you have changed a row in the result set. And of course, 
after you
have changed an item from the resultset into a list, the list will not know
anything whatsoever about any database, making this problem even more

>What I think has to happen at the moment is each record must be
>converted to a dict and then after I can change each record based on the
>key of the dict.  This does not sound right - I'm doing something wrong.
The following code is untested and uses a lot of different python
concepts that you may not have encountered yet. But I hope this gives
an idea about the kind of work that you have to do to make this work
the way you want.

class DBRow:
    """An editable row that remembers from which row in the table it came"""
    def __init__(self, oid, cols, data):
        self.oid = oid      # row identifier
        self.cols = cols    # column names (a list)
        self.data = data    # row data (a list)
        self.dirty = 0      # only save when updated

    def __getitem__(self, key):
        # used to retrieve a value for one column in this row
        index = self.cols.index(key)
        return self.data[index]

    def __setitem__(self, key, value):
        # used to set a value for one columns in this row
        index = self.cols.index(key)
        self.data[index] = value
        self.dirty = 1

    def save(self, table_name, cursor):
        if not self.dirty:

        terms = []
        for col, val in zip(self.cols, self.data)
             terms.append("%s=%s" % (col, val))

        cmnd = "UPDATE %s SET %s WHERE oid=%s"  % (table_name, terms, 

class DBTable:
    """Represent the table as a list of editable DBRows"""
    def __init__(self, db, table_name, clause=''):
        self.table_name = table_name
        self.db = db
        self.rows = []

        # get the data from the db
        cmnd = "SELECT oid, * FROM %s" % table_name
        if clause:
            cmnd = cmnd+" WHERE %s" % clause
        c = db.cursor()
        # find names, except oid
        colnames = [d[0] for d in c.description[1:]]

        # transform the resultset in a list of DBRow objects
        for row in c.fetchall():
            oid = row[0]
            data = row[1:]
            self.rows.append(DBRow(oid, colnames, data))

    def __getitem__(self, index):
        return self.rows[index]

    def save(self):
        c = self.db.cursor()
        for r in self.rows:
            r.save(self.table_name, c)

With this you should be able to do something like:

 >>> db = PgSQL.connect(...)
 >>> data = DBTable(db, "mytable", "aaa>42")
 >>> for row in data:
...     row['aaa'] = row['bbb']+3*row['ccc']
 >>> data.save()

which would change column aaa for all rows where aaa>42

Note that

db = PgSQL.connect(...)
c = db.cursor()
c.execute("UPDATE mytable SET aaa=bbb+3*ccc WHERE aaa>42")

Is by far the fastest (if not easiest) way to solve this particular

Hope this helps,


More information about the Tutor mailing list