Welcome, guest | Sign In | My Account | Store | Cart

A lightweight method to access the field names from a database.

Python, 88 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import types

class FieldNameError(Exception):
  def __init__(self, value):
    self.value = value
  def __str__(self):
    return `"""Field name '%s' does not exist""" % self.value`


class fRow(tuple):
  # class for each row
  
  def __getattr__(self,i):
    return tuple.__getitem__(self,self.__Field2Index__(i))

  def __call__(self,i):
    return tuple.__getitem__(self,self.__Field2Index__(i))

  def __getitem__(self,i):
    if type(i) != types.SliceType:
      return tuple.__getitem__(self,self.__Field2Index__(i))
    else:
      if i.start is not None and i.stop is not None:
        return self[self.__Field2Index__(i.start):self.__Field2Index__(i.stop)]
      elif i.start is None:
        return self[:self.__Field2Index__(i.stop)]
      elif i.stop is None:
        return self[self.__Field2Index__(i.start):]
      else:
        return self[:]

  def __Field2Index__():
    return None

class fRowset(list):
  # list to hold the rows
  
  def __init__(self,rowset,description):
    # save the description as is
    self.description = fRow(description)
    self.description.__Field2Index__ = self.__fieldToIndex
    
    # Create the list and dict of fields
    self.fields = []
    self.__fieldDict = {}
    for f in range(len(description)):
      if type(description[f]) == types.TupleType or type(description[f]) == types.ListType:
        self.__fieldDict[description[f][0].lower()] = f
        self.fields.append( description[f][0].lower())
      else:
        self.__fieldDict[description[f].lower()] = f
        self.fields.append( description[f].lower())
    # Add all the rows
    for r in rowset:
      self.append(r)  

  def append(self,new):
    # Create a new record
    fR = fRow(new)
    # Pass it the function that looks up the index
    fR.__Field2Index__ = self.__fieldToIndex
    list.append(self,fR)
    return

  # Look up the field and return the index  
  def __fieldToIndex(self,field):
    if type(field) == int:
      return field
    try:
      return self.__fieldDict[field.lower()]
    except:
      raise FieldNameError, field


def ffetchall(cursor):
  # Nice wrapper for fetchall
  return fRowset(cursor.fetchall(),cursor.description)

def ffetchmany(cursor):
  # Nice wrapper for fetchmany
  return fRowset(cursor.fetchmany(),cursor.description)

def fquery(connection,query):
  curs = connection.cursor()
  curs.execute(query)
  rows  = fRowset(curs.fetchall(),curs.description)
  curs.close()
  return rows

I'm new to Python so I may have missed something here.

This is a lightweight method of accessing the columns by the field names along with the column number. The cursor description and the dictionary of field names are stored in the list that wraps the tuples. Other then the field names they act like a list of tuples.

You can use the field names just about any way and they are case insensitive. The following all return the same data.

print row1[0][1], row10,row1[0].name, row1[0].NaMe, row10, row1[0]['nAme'], row1[0]['name':][0]

Some of the other uses:

Creating the rowsets

import odbc

dbconn = odbc.odbc(Deploy/sa/') row1 = fquery(dbconn,"select * from Categories order by name")

cursor = dbconn.cursor() cursor.execute("select Areacode from AreaCodes") row2 = ffetchall(cursor) cursor.close()

for fn in row1.fields: print fn,'=',row1[2][fn],row12

for rw in row1[2:18]: print ' %(number)s = %(name)s ' % rw

Build a new one using a list of strings for the description

row3 = fRowset([],['newName','newNumber'])

for rw in row1[5:25]: row3.append([rw.name,rw.number])

print row3[3].newName

Slice with field names

print row1[0]['name':'sort']

print 'Length: ',len(row1),len(row2) print 'row2 fields:\n\t',row2.fields print 'row1 description:\n\t',row1.description

Prints the description for the specified field

print row1.description.number print row1.description.name[1]

The only issue is that this is much slower than the built-in list of tuples. I used this format for timing tests.

t1 = time.time() for i in xrange(100000): r = row1[0][1] print """fRowset "r=fRows[0][1]" : """,time.time() - t1

rows is a built-in list of tuples. fRows is a fRowset.

built-in "r=rows[0][1]" : 0.110000014305 fRowset "r=fRows[0][1]" : 1.58200001717 fRowset "r=fRows0" : 1.3220000267 fRowset "r=fRows0" : 1.70299994946 fRowset "r=fRows[0]['name']" : 1.93200004101 fRowset "r=fRows[0]['NAMe']" : 1.97300004959 fRowset "r=fRows[0].name" : 2.20299994946 fRowset "r=fRows[0][row1.fields.index('name')]" : 1.85300004482 fRowset "r=fRows[0][:'name']" : 2.97399997711

Soooooo...

If you need to manipulate a lot of data use the built-in. For small data sets and readability this seems to work well.

I haven't run this is the "wild" so any thoughts and comments would be appreciated.

Created by Jim Kraai on Thu, 18 Sep 2003 (PSF)
Python recipes (4591)
Jim Kraai's recipes (1)

Required Modules

Other Information and Tasks