Як мені серіалізувати вихід курсору pyodbc (з .fetchone
, .fetchmany
або .fetchall
) як словник Python?
Я використовую пляшку і мені потрібно повернути dict, щоб він міг повернути його як JSON.
Як мені серіалізувати вихід курсору pyodbc (з .fetchone
, .fetchmany
або .fetchall
) як словник Python?
Я використовую пляшку і мені потрібно повернути dict, щоб він міг повернути його як JSON.
Відповіді:
Якщо ви не знаєте стовпців заздалегідь, використовуйте Cursor.description, щоб створити список назв стовпців та zip з кожним рядком, щоб створити список словників. Приклад передбачає, що зв’язок та запит побудовані:
>>> 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
. це просто врятувало мені багато часу.
Використовуючи результат @ Beargle з бутлявим, я зміг створити цей дуже стислий запит, що викриває кінцеву точку:
@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()]}
Ось коротка версія, яку ви могли б використовувати
>>> 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()]
Як ви могли знати, додаючи * до списку, ви в основному видаляєте список, залишаючи окремі записи списку як параметри функції, яку ви викликаєте. За допомогою застібки-блискавки ми обираємо 1-й до n-й запис і стискаємо їх разом, як застібку-блискавку у Ваших штанах.
так за допомогою
zip(*[(a,1,2),(b,1,2)])
# interpreted by python as zip((a,1,2),(b,1,2))
ти отримуєш
[('a', 'b'), (1, 1), (2, 2)]
Оскільки опис - кортеж із кортежами, де кожен кортеж описує заголовок та тип даних для кожного стовпця, ви можете витягти перший із кожного кортежу за допомогою
>>> columns = zip(*cursor.description)[0]
дорівнює
>>> columns = [column[0] for column in cursor.description]
TypeError: 'zip' object is not subscriptable
тому я не можу використовувати zip(*description)[0]
фокус.
columns
змінною, але помножили складність функції, обчислюючи назви стовпців для кожного рядка окремо
Переважно відходячи від відповіді @Torxed, я створив повний узагальнений набір функцій для пошуку схеми та даних у словнику:
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))
Не соромтеся пройти весь код-гольф на цьому, щоб зменшити лінії; але тим часом це працює!
;)
У ситуаціях, коли курсор недоступний - наприклад, коли рядки були повернуті за допомогою якогось виклику функції або внутрішнього методу, ви все ще можете створити подання словника за допомогою row.cursor_description
def row_to_dict(row):
return dict(zip([t[0] for t in row.cursor_description], row))
Я знаю, що це питання давнє, але воно допомогло мені зрозуміти, як зробити те, що мені потрібно, і воно дещо відрізняється від того, про що просив ОП, тому я подумав поділитися, щоб допомогти кожному, хто потребує того, що мені потрібно: Якщо ви хочете повністю узагальнити підпрограму, яка виконує запити вибору SQL, але вам потрібно посилатися на результати за номером індексу, а не за іменем, це можна зробити зі списком списків замість словника. Кожен рядок повернутих даних представлений у поверненому списку як список значень полів (стовпців). Імена стовпців можна вказати як перший запис у списку, що повертається, тому аналіз списку, що повертається, у процедурі виклику може бути дуже простим та гнучким. Таким чином, програма, що виконує виклик бази даних, не повинна знати нічого про дані, якими вона обробляє. Ось така рутина:
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
Мені подобаються відповіді @bryan та @ foo-stack. Якщо ви працюєте з postgresql, і ви використовуєте його, psycopg2
ви можете використати деякі смаколики з psycopg2, щоб досягти цього, вказавши cursorfactory як a DictCursor
при створенні курсору із з'єднання, наприклад:
cur = conn.cursor( cursor_factory=psycopg2.extras.DictCursor )
Отже, тепер ви можете виконати свій SQL-запит, і ви отримаєте словник для отримання результатів без необхідності їх картографувати вручну.
cur.execute( sql_query )
results = cur.fetchall()
for row in results:
print row['row_no']
Зверніть увагу, що вам доведеться, щоб import psycopg2.extras
це працювало.
Якщо припустити, що ви знаєте свої назви стовпців! Крім того, ось три різні рішення,
напевно , ви хочете поглянути на останнє!
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
Це індексована версія, не найкрасивіше рішення, але вона буде працювати. Іншим методом було б індексувати назву стовпця як ключ словника зі списком у кожному ключі, що містить дані в порядку номера рядка. виконуючи:
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
Написавши це, я розумію, що робити це for col in colnames
можна замінити, for colindex in range(0, len())
але ти розумієш . Пізніший приклад tho був би корисний, коли не отримуватиметься всі дані, а один рядок за раз, наприклад:
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']
Отримання імен таблиць (я думаю .. завдяки Foo Stack):
більш пряме рішення від Beargle нижче!
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]]