¿Cómo puedo serializar salida del cursor pyodbc (de .fetchone
, .fetchmany
o .fetchall
) como un diccionario de Python?
Estoy usando bottlepy y necesito devolver dict para que pueda devolverlo como JSON.
¿Cómo puedo serializar salida del cursor pyodbc (de .fetchone
, .fetchmany
o .fetchall
) como un diccionario de Python?
Estoy usando bottlepy y necesito devolver dict para que pueda devolverlo como JSON.
Respuestas:
Si usted no sabe columnas antes de tiempo, utilizar Cursor.description para construir una lista de nombres de columna y cremallera con cada fila para producir una lista de diccionarios. El ejemplo asume que la conexión y la consulta están construidas:
>>> cursor = connection.cursor().execute(sql)
>>> columns = [column[0] for column in cursor.description]
>>> print(columns)
['name', 'create_date']
>>> results = []
>>> for row in cursor.fetchall():
... results.append(dict(zip(columns, row)))
...
>>> print(results)
[{'create_date': datetime.datetime(2003, 4, 8, 9, 13, 36, 390000), 'name': u'master'},
{'create_date': datetime.datetime(2013, 1, 30, 12, 31, 40, 340000), 'name': u'tempdb'},
{'create_date': datetime.datetime(2003, 4, 8, 9, 13, 36, 390000), 'name': u'model'},
{'create_date': datetime.datetime(2010, 4, 2, 17, 35, 8, 970000), 'name': u'msdb'}]
cursor.description
. esto me ahorró un montón de tiempo.
Usando el resultado de @ Beargle con bottlepy, pude crear esta consulta muy concisa que expone el punto final:
@route('/api/query/<query_str>')
def query(query_str):
cursor.execute(query_str)
return {'results':
[dict(zip([column[0] for column in cursor.description], row))
for row in cursor.fetchall()]}
Aquí hay una versión de formato corto que podría usar
>>> cursor.select("<your SQL here>")
>>> single_row = dict(zip(zip(*cursor.description)[0], cursor.fetchone()))
>>> multiple_rows = [dict(zip(zip(*cursor.description)[0], row)) for row in cursor.fetchall()]
Como ya sabrá, cuando agrega * a una lista, básicamente elimina la lista, dejando las entradas individuales de la lista como parámetros para la función que está llamando. Al usar la cremallera, seleccionamos la entrada 1 an y la juntamos como la cremallera de tus pantalones.
entonces usando
zip(*[(a,1,2),(b,1,2)])
# interpreted by python as zip((a,1,2),(b,1,2))
usted obtiene
[('a', 'b'), (1, 1), (2, 2)]
Dado que la descripción es una tupla con tuplas, donde cada tupla describe el encabezado y el tipo de datos de cada columna, puede extraer el primero de cada tupla con
>>> columns = zip(*cursor.description)[0]
equivalente a
>>> columns = [column[0] for column in cursor.description]
TypeError: 'zip' object is not subscriptable
así que no puedo usar el zip(*description)[0]
truco.
columns
variable, pero multiplicó la complejidad de la función al calcular los nombres de las columnas para cada fila por separado
Principalmente saliendo de la respuesta @Torxed, creé un conjunto completo de funciones generalizadas para encontrar el esquema y los datos en un diccionario:
def schema_dict(cursor):
cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';")
schema = {}
for it in cursor.fetchall():
if it[0] not in schema:
schema[it[0]]={'scheme':[]}
else:
schema[it[0]]['scheme'].append(it[1])
return schema
def populate_dict(cursor, schema):
for i in schema.keys():
cursor.execute("select * from {table};".format(table=i))
for row in cursor.fetchall():
colindex = 0
for col in schema[i]['scheme']:
if not 'data' in schema[i]:
schema[i]['data']=[]
schema[i]['data'].append(row[colindex])
colindex += 1
return schema
def database_to_dict():
cursor = connect()
schema = populate_dict(cursor, schema_dict(cursor))
Siéntase libre de hacer todo el código de golf en esto para reducir las líneas; pero mientras tanto, ¡funciona!
;)
Para situaciones en las que el cursor no está disponible, por ejemplo, cuando las filas han sido devueltas por alguna llamada de función o método interno, aún puede crear una representación de diccionario usando row.cursor_description
def row_to_dict(row):
return dict(zip([t[0] for t in row.cursor_description], row))
Sé que esta pregunta es antigua, pero me ayudó a descubrir cómo hacer lo que necesitaba, que es ligeramente diferente de lo que pedía OP, así que pensé en compartir, para ayudar a cualquier otra persona que necesite lo que yo necesitaba: Si desea generalizar completamente una rutina que realiza consultas de selección de SQL, pero necesita hacer referencia a los resultados mediante un número de índice, no un nombre, puede hacerlo con una lista de listas en lugar de un diccionario. Cada fila de datos devueltos se representa en la lista devuelta como una lista de valores de campo (columna). Los nombres de las columnas se pueden proporcionar como la primera entrada de la lista devuelta, por lo que analizar la lista devuelta en la rutina de llamada puede ser realmente fácil y flexible. De esta manera, la rutina que realiza la llamada a la base de datos no necesita saber nada sobre los datos que está manejando. Aquí hay una rutina de este tipo:
def read_DB_Records(self, tablename, fieldlist, wherefield, wherevalue) -> list:
DBfile = 'C:/DATA/MyDatabase.accdb'
# this connection string is for Access 2007, 2010 or later .accdb files
conn = pyodbc.connect(r'Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ='+DBfile)
cursor = conn.cursor()
# Build the SQL Query string using the passed-in field list:
SQL = "SELECT "
for i in range(0, len(fieldlist)):
SQL = SQL + "[" + fieldlist[i] + "]"
if i < (len(fieldlist)-1):
SQL = SQL + ", "
SQL = SQL + " FROM " + tablename
# Support an optional WHERE clause:
if wherefield != "" and wherevalue != "" :
SQL = SQL + " WHERE [" + wherefield + "] = " + "'" + wherevalue + "';"
results = [] # Create the results list object
cursor.execute(SQL) # Execute the Query
# (Optional) Get a list of the column names returned from the query:
columns = [column[0] for column in cursor.description]
results.append(columns) # append the column names to the return list
# Now add each row as a list of column data to the results list
for row in cursor.fetchall(): # iterate over the cursor
results.append(list(row)) # add the row as a list to the list of lists
cursor.close() # close the cursor
conn.close() # close the DB connection
return results # return the list of lists
Me gustan las respuestas de @bryan y @ foo-stack. Si está trabajando con postgresql y está usando psycopg2
, podría usar algunos beneficios de psycopg2 para lograr lo mismo especificando que el cursorfactory sea a DictCursor
al crear su cursor desde la conexión, así:
cur = conn.cursor( cursor_factory=psycopg2.extras.DictCursor )
Así que ahora puede ejecutar su consulta SQL y obtendrá un diccionario para obtener sus resultados, sin la necesidad de mapearlos a mano.
cur.execute( sql_query )
results = cur.fetchall()
for row in results:
print row['row_no']
Tenga en cuenta que tendrá que hacerlo import psycopg2.extras
para que funcione.
¡Suponiendo que conozca los nombres de las columnas! Además, aquí hay tres soluciones diferentes,
¡probablemente quieras ver la última!
colnames = ['city', 'area', 'street']
data = {}
counter = 0
for row in x.fetchall():
if not counter in data:
data[counter] = {}
colcounter = 0
for colname in colnames:
data[counter][colname] = row[colcounter]
colcounter += 1
counter += 1
Esa es una versión indexada, no es la solución más hermosa, pero funcionará. Otra sería indexar el nombre de la columna como clave de diccionario con una lista dentro de cada clave que contiene los datos en orden de número de fila. haciendo:
colnames = ['city', 'area', 'street']
data = {}
for row in x.fetchall():
colindex = 0
for col in colnames:
if not col in data:
data[col] = []
data[col].append(row[colindex])
colindex += 1
Al escribir esto, entiendo que hacer for col in colnames
podría ser reemplazado por, for colindex in range(0, len())
pero entiendes la idea. El último ejemplo sería útil cuando no se obtienen todos los datos, sino una fila a la vez, por ejemplo:
def fetchone_dict(stuff):
colnames = ['city', 'area', 'street']
data = {}
for colindex in range(0, colnames):
data[colnames[colindex]] = stuff[colindex]
return data
row = x.fetchone()
print fetchone_dict(row)['city']
Obtener nombres de tablas (creo ... gracias a Foo Stack): ¡
una solución más directa de Beargle a continuación!
cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';")
schema = {}
for it in cursor.fetchall():
if it[0] in schema:
schema[it[0]].append(it[1])
else:
schema[it[0]] = [it[1]]
cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';") schema = {} for it in cursor.fetchall(): if it[0] in schema: schema[it[0]].append(it[1]) else: schema[it[0]] = [it[1]]