A lightweight method to access the field names from a database.
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.