Flask 實現 Rest API (02)
Rest API 以 json 格式對 Request 進行響應。結果集是如何轉換成 json 呢?上一篇我們是這樣做的:
# 首先得到一個 dict
items = [dict((curr.description[i][0], value)
for i, value in enumerate(row))
for row in curr.fetchall()]
# 然後通過 flask 的 jsonify 轉換成 json 格式
employees = emp.listAll()
return jsonify({'rows': employees}), 200;
這幾句程式碼的資訊量太大,我打算再用一篇文章來解釋,順便解決程式碼中存在的一個小問題。
獲取欄位名
Python 的 DB-API 規定,curso
r 物件的 description
屬性應該返回一個序列 (sequence),這個序列包括下面的內容:
- name
- type_code
- display_size
- internal_size
- precision
- scale
- null_ok
第一個元素的 name
就是我們要的 column name
。據此,可以編寫一個函式得到 column name
:
import pymysql
def get_connection():
db = pymysql.connect('localhost', 'root', 'pwd', 'stonetest')
return db
def get_column_names(sql):
conn = get_connection()
cur = conn.cursor()
cur.execute(sql)
col_names = []
column_count = len(cur.description)
for i in range(column_count):
desc = cur.description[i]
col_names.append(desc[0])
return col_names
col_names = get_column_names('select * from users;')
print (col_names)
寫的這麼囉嗦是為了解釋 column name 是如何獲得的。通過除錯可以發現,cur.description
根據 resultset
的每一列,建立了一個 7-item 的元組 (tuple), name
處在 index 為 0 的位置。然後所有列的元組再組合成一個元組,如下圖所示:
所以我們用上面的方法,得到列名的集合,以 list
型別返回。get_column_names
函式用的是常規的迴圈方法,其實 Python 提供了很好的機制,讓我們可以大大簡化程式碼。需要用到 enumerate
函式和列表推導式。先 兩個相關相關知識做一個簡單說明。
enumerate
函式
enurmerate
函式是 Python 的內建函式,enurmerate
作用於序列或者迭代器 (iterator),返回一個新的迭代器,同時自動維護一個內部的計數器,從而簡化迴圈的語句的編寫。比如從 basic_colors
的列表中,遍歷得到每一種顏色,為了迴圈,我們構造了一個計數器 i
:
basic_colors = ['red', 'green', 'blue']
for i in range(len(basic_colors)):
print (i, basic_colors[i])
使用 enumerate
函式後,程式碼可以簡化為:
basic_colors = ['red', 'green', 'blue']
for i, item in enumerate(basic_colors):
print (i, item)
列表推導式
列表推導式提供了一種簡明的方法來建立列表。列表推導式在有些情況下特別方便,特別是當你需要使用 for
迴圈來生成一個新列表時。比如:
days = []
for i in range(1, 32):
days.append(i)
print (days)
# output: [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]
可以使用列表推導式簡化為:
days = [i for i in range(1, 32)]
print (days)
有了以上兩個工具,get_column_names
函式可以簡化為:
def get_column_names(sql):
conn = get_connection()
cur = conn.cursor()
cur.execute(sql)
column_names = [item[0] for counter, item in enumerate(cur.description) ]
return column_names
現在應該理解了最剛開始的 items
語句:
items = [dict((curr.description[i][0], value)
for i, value in enumerate(row))
for row in curr.fetchall()]
翻譯一下,就是下面的程式碼:
def list_all():
# ...
rows = cur.fetchall()
rows_list = []
for row in rows:
line_dict = dict()
for i in range(len(row)):
line_dict[cur.description[i][0]] = row[i]
rows_list.append(line_dict)
return rows_list
解決欄位順序問題
以上方法得到的 json 字串,json 字串中 key
的順序與表中欄位
的順序並不一致,原因有兩點:
dict
中key
是沒有順序的,dict
使用key
來查詢,所以輸出的順序隨機;- flask 的
jsonfify()
方法在輸出欄位時,受app.config['JSON_SORT_KEYS']
控制,預設為True
,所以一般情況下 json 的輸出是key
的字母順序輸出的。jsonfiy()
方法會同時將minetype
設定為application/json
。
知道問題的所在,解決辦法也很簡單:
app.config['JSON_SORT_KEYS']
設為False
listAll()
方法中,返回值用dict
的子類OrderedDict
。改寫後的listAll()
方法如下:
def listAll(self):
conn = db.connect()
curr = conn.cursor()
curr.callproc('getEmployees')
col_names = [desc[0] for desc in curr.description]
result = []
for row in curr.fetchall():
dict_obj = OrderedDict()
for i, v in enumerate(row): # index, value
dict_obj[col_names[i]] = v
result.append(dict_obj)
curr.close()
conn.close()
return result