在數據庫操做時,新增記錄也是必不可少的,接下來咱們應用字典的特性來組合sql語句python
先上產品新增接口代碼web
1 @post('/api/product/') 2 def callback(): 3 """ 4 新增記錄 5 """ 6 name = web_helper.get_form('name', '產品名稱') 7 code = web_helper.get_form('code', '產品編碼') 8 product_class_id = convert_helper.to_int0(web_helper.get_form('product_class_id', '產品分類')) 9 standard = web_helper.get_form('standard', '產品規格') 10 quality_guarantee_period = web_helper.get_form('quality_guarantee_period', '保質期') 11 place_of_origin = web_helper.get_form('place_of_origin', '產地') 12 front_cover_img = web_helper.get_form('front_cover_img', '封面圖片') 13 content = web_helper.get_form('content', '產品描述', is_check_special_char=False) 14 # 防sql注入攻擊處理 15 content = string_helper.filter_str(content, "'") 16 # 防xss攻擊處理 17 content = string_helper.clear_xss(content) 18 is_enable = convert_helper.to_int0(web_helper.get_form('is_enable', '是否啓用')) 19 20 # 添加記錄(使用returning這個函數能返回指定的字段值,這裏要求返回新添加記錄的自增id值) 21 sql = """insert into product (name, code, product_class_id, standard, quality_guarantee_period, 22 place_of_origin, front_cover_img, content, is_enable) 23 values (%s, %s, %s, %s, %s, %s, %s, %s, %s) returning id""" 24 vars = (name, code, product_class_id, standard, quality_guarantee_period, place_of_origin, front_cover_img, content, is_enable) 25 # 寫入數據庫 26 result = db_helper.write(sql, vars) 27 # 判斷是否提交成功 28 if result and result[0].get('id'): 29 return web_helper.return_msg(0, '成功') 30 else: 31 return web_helper.return_msg(-1, "提交失敗")
在21行到24行就是sql語句的拼接,使用這種方法,咱們常常會由於多寫或少寫%s和變量,致使sql執行出錯。sql
在python中,咱們最經常使用的就是字典,重新增記錄的語句中,能夠看到字段與值是一一對應的,那麼咱們就能夠考慮使用字典的方式來進行存儲字段與值,在ORM中對其進行處理,而後拼接生成對應的sql語句。數據庫
操做步驟以下幾步:api
1.將新增記錄時的字段名與值,使用字典方式存儲起來數組
2.將字典作爲參數傳給ORM新增記錄方法app
3.在進行下一步以前,你們首先要了解python字符串替的有多種方法,例如:方法一:’Hello %s' % ('world',) ;方法二:'Hello %(name)s' % {'name': 'world'} ;等,還有不少其餘方法這裏不一一列舉。第一種只能按照指定的順序進行替換,數量必須一致,否則會出錯;而第二種字典類型替換時,字典位置不須要考慮,字典值超出替換內容,會自動忽略,它最大的好處,我以爲是可讀性,讓這個字符串變的更加容易理解。因此下面咱們將會使用字典替換方法。xss
4.新增記錄方法接收到參數之後,使用for循環,將字段名提取出來,生成sql插入字段名數組和字典替換數組,即:insert into table_name (字段名,字段名...) values (值,值...),這裏須要將字典中的字段名提取出來組合成「字段名,字段名...」這樣的串,值也是同樣,爲了讓組合後的字符串更容易調試與理解,咱們將它生成字典替換格式%(name1)s,%(name2)s...,最好再使用字典直接替換(固然你直接將值鏈接起來也行,你能夠根據本身的須要進行調整)。函數
例如,咱們須要添加產品名稱、產品編碼、產品分類id,能夠它們用字典存儲起來作爲參數post
fields = { 'name': "'名稱'", 'code': "'201808031245678'", 'product_class_id': 1, }
而後能夠經過for循環,將字典參數進行處理,提取出來存儲到list中
key_list = [] value_list = [] for key in fields.keys(): key_list.append(key) value_list.append('%(' + key + ')s')
執行後,key_list與value_list的值分別爲:
key_list = ['name', 'product_class_id', 'code'] value_list = ['%(name)s', '%(product_class_id)s', '%(code)s']
而後咱們設置一個sql字符串拼接字典,將表名、字段名字符串與值字符串存儲進去,在存儲前使用join方式進行拼接,生成用逗號分隔的字符串
parameter = { 'table_name': self.__table_name, 'key_list': ','.join(key_list), 'value_list': ','.join(value_list) }
執行後生成的值爲:
parameter = {'key_list': 'name,product_class_id,code', 'value_list': '%(name)s,%(product_class_id)s,%(code)s', 'table_name': 'product'}
而後將它們與新增sql合成
sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning id " % parameter
執行後sql值爲:
sql = 'insert into product (name,product_class_id,code) values (%(name)s,%(product_class_id)s,%(code)s) '
最後將它與最開始提交的字典參數進行合成
sql = sql % fields
生成最終可執行的sql語句
sql = "insert into product (name,product_class_id,code) values ('名稱',1,'201808031245678') "
完整代碼
1 def add_model(self, fields, returning=''): 2 """新增數據庫記錄""" 3 ### 拼接sql語句 ### 4 # 初始化變量 5 key_list = [] 6 value_list = [] 7 # 將傳入的字典參數進行處理,把字段名生成sql插入字段名數組和字典替換數組 8 # PS:字符串使用字典替換參數時,格式是%(name)s,這裏會生成對應的字串 9 # 好比: 10 # 傳入的字典爲: {'id': 1, 'name': '名稱'} 11 # 那麼生成的key_list爲:'id','name' 12 # 而value_list爲:'%(id)s,%(name)s' 13 # 最終而value_list爲字符串對應名稱位置會被替換成相應的值 14 for key in fields.keys(): 15 key_list.append(key) 16 value_list.append('%(' + key + ')s') 17 # 設置sql拼接字典,並將數組(lit)使用join方式進行拼接,生成用逗號分隔的字符串 18 parameter = { 19 'table_name': self.__table_name, 20 'pk_name': self.__pk_name, 21 'key_list': ','.join(key_list), 22 'value_list': ','.join(value_list) 23 } 24 # 若是有指定返回參數,則添加 25 if returning: 26 parameter['returning'] = ', ' + returning 27 else: 28 parameter['returning'] = '' 29 30 # 生成可使用字典替換的字符串 31 sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning %(pk_name)s %(returning)s" % parameter 32 # 將生成好的字符串替字典參數值,生成最終可執行的sql語句 33 sql = sql % fields 34 35 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 36 # 執行sql語句 37 result = db.execute(sql) 38 if result: 39 db.commit() 40 return result[0] 41 return {}
代碼你們多debug一下,就能夠查看到每一行代碼的執行狀況以及sql拼接生成狀況了。
這裏的sql語句額外添加了postgresql的returning函數,在默認狀況下會返回數據插入成功後的主鍵值給主程序。咱們在進行新增操做時,常常要獲取新增記錄的自增id,經過return id就能夠將它返回給咱們,若是你還須要返回其餘字段值,能夠在這個新增方式的returning參數中添加想要返回的字段名,若是想返回整條記錄,輸入*號就能夠了。
寫到這裏,能夠看到查詢與執行,都使用相似的with方法,咱們能夠將它們提取出來重構成獨立的函數,爲後續的修改和其餘查詢去除重複代碼,改造後代碼以下:
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 from common import db_helper 5 6 7 class LogicBase(): 8 """邏輯層基礎類""" 9 10 def __init__(self, db, is_output_sql, table_name, column_name_list='*', pk_name='id'): 11 """類初始化""" 12 # 數據庫參數 13 self.__db = db 14 # 是否輸出執行的Sql語句到日誌中 15 self.__is_output_sql = is_output_sql 16 # 表名稱 17 self.__table_name = str(table_name).lower() 18 # 查詢的列字段名稱,*表示查詢所有字段,多於1個字段時用逗號進行分隔,除了字段名外,也能夠是表達式 19 self.__column_name_list = str(column_name_list).lower() 20 # 主健名稱 21 self.__pk_name = str(pk_name).lower() 22 23 ##################################################################### 24 ### 執行Sql ### 25 26 def get_model(self, wheres): 27 """經過條件獲取一條記錄""" 28 # 若是有條件,則自動添加where 29 if wheres: 30 wheres = ' where ' + wheres 31 32 # 合成sql語句 33 sql = "select %(column_name_list)s from %(table_name)s %(wheres)s" % \ 34 {'column_name_list': self.__column_name_list, 'table_name': self.__table_name, 'wheres': wheres} 35 # 初化化數據庫連接 36 result = self.select(sql) 37 if result: 38 return result[0] 39 return {} 40 41 def get_model_for_pk(self, pk, wheres=''): 42 """經過主鍵值獲取數據庫記錄實體""" 43 if not pk: 44 return {} 45 # 組裝查詢條件 46 wheres = '%s = %s' % (self.__pk_name, str(pk)) 47 48 return self.get_model(wheres) 49 50 def add_model(self, fields, returning=''): 51 """新增數據庫記錄""" 52 ### 拼接sql語句 ### 53 # 初始化變量 54 key_list = [] 55 value_list = [] 56 # 將傳入的字典參數進行處理,把字段名生成sql插入字段名數組和字典替換數組 57 # PS:字符串使用字典替換參數時,格式是%(name)s,這裏會生成對應的字串 58 # 好比: 59 # 傳入的字典爲: {'id': 1, 'name': '名稱'} 60 # 那麼生成的key_list爲:'id','name' 61 # 而value_list爲:'%(id)s,%(name)s' 62 # 最終而value_list爲字符串對應名稱位置會被替換成相應的值 63 for key in fields.keys(): 64 key_list.append(key) 65 value_list.append('%(' + key + ')s') 66 # 設置sql拼接字典,並將數組(lit)使用join方式進行拼接,生成用逗號分隔的字符串 67 parameter = { 68 'table_name': self.__table_name, 69 'pk_name': self.__pk_name, 70 'key_list': ','.join(key_list), 71 'value_list': ','.join(value_list) 72 } 73 # 若是有指定返回參數,則添加 74 if returning: 75 parameter['returning'] = ', ' + returning 76 else: 77 parameter['returning'] = '' 78 79 # 生成可使用字典替換的字符串 80 sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning %(pk_name)s %(returning)s" % parameter 81 # 將生成好的字符串替字典參數值,生成最終可執行的sql語句 82 sql = sql % fields 83 84 result = self.execute(sql) 85 if result: 86 return result[0] 87 return {} 88 89 def select(self, sql): 90 """執行sql查詢語句(select)""" 91 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 92 # 執行sql語句 93 result = db.execute(sql) 94 if not result: 95 result = [] 96 return result 97 98 def execute(self, sql): 99 """執行sql語句,並提交事務""" 100 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 101 # 執行sql語句 102 result = db.execute(sql) 103 if result: 104 db.commit() 105 else: 106 result = [] 107 return result 108 109 #####################################################################
上單元測試代碼:
#!/usr/bin/evn python # coding=utf-8 import unittest from common.string_helper import string from logic import product_logic class DbHelperTest(unittest.TestCase): """數據庫操做包測試類""" def setUp(self): """初始化測試環境""" print('------ini------') def tearDown(self): """清理測試環境""" print('------clear------') def test(self): ############################################## # 只須要看這裏,其餘代碼是測試用例的模板代碼 # ############################################## # 實例化product表操做類ProductLogic _product_logic = product_logic.ProductLogic() # 執行get_model_for_pk()方法,獲取記錄實體 model = _product_logic.get_model_for_pk(2) print(model) # 測試新增記錄 fields = { 'name': string('名稱'), 'code': string('201808031245678'), 'product_class_id': 1, } result = _product_logic.add_model(fields, '*') print(result) ############################################## if __name__ == '__main__': unittest.main()
PS:單元測試中,經過from common.string_helper import string導入了string這個方法,它是用來給字符串添加單撇號字符(')用的,由於在更新數據庫時,字符串類型須要用單撇號括起來的,而Python中直接使用單撇字符只是標識它爲字符串而已,須要用\'或雙引號+單撇字符+雙引號方式,才能獲得咱們想要的結果,爲了方便操做,因此增長了string這個方法,幫咱們進行轉換。
輸出結果:
------ini------ {'content': '產品描述', 'name': '餅乾', 'place_of_origin': '廣東廣州', 'add_time': datetime.datetime(2018, 7, 25, 23, 10, 4), 'code': '20180212321211', 'is_enable': 1, 'front_cover_img': 'http://xxx.com/xxx.jpg', 'product_class_id': 1, 'quality_guarantee_period': '2018年12月', 'id': 2, 'standard': '500g'} {'content': '', 'name': '名稱', 'place_of_origin': '', 'add_time': datetime.datetime(2018, 8, 3, 16, 51, 3), 'code': '201808031245678', 'is_enable': 0, 'front_cover_img': '', 'product_class_id': 1, 'quality_guarantee_period': '', 'id': 15, 'standard': ''} ------clear------
前面的接口也能夠改造爲
1 @post('/api/product/') 2 def callback(): 3 """ 4 新增記錄 5 """ 6 name = web_helper.get_form('name', '產品名稱') 7 code = web_helper.get_form('code', '產品編碼') 8 product_class_id = convert_helper.to_int0(web_helper.get_form('product_class_id', '產品分類')) 9 standard = web_helper.get_form('standard', '產品規格') 10 quality_guarantee_period = web_helper.get_form('quality_guarantee_period', '保質期') 11 place_of_origin = web_helper.get_form('place_of_origin', '產地') 12 front_cover_img = web_helper.get_form('front_cover_img', '封面圖片') 13 content = web_helper.get_form('content', '產品描述', is_check_special_char=False) 14 # 防sql注入攻擊處理 15 content = string_helper.filter_str(content, "'") 16 # 防xss攻擊處理 17 content = string_helper.clear_xss(content) 18 is_enable = convert_helper.to_int0(web_helper.get_form('is_enable', '是否啓用')) 19 20 # 設置新增參數 21 fields = { 22 'name': string(name), 23 'code': string(code), 24 'product_class_id': product_class_id, 25 'standard': string(standard), 26 'quality_guarantee_period': string(quality_guarantee_period), 27 'place_of_origin': string(place_of_origin), 28 'front_cover_img': string(front_cover_img), 29 'content': string(content), 30 'is_enable': is_enable, 31 } 32 # 實例化product表操做類ProductLogic 33 _product_logic = product_logic.ProductLogic() 34 # 新增記錄 35 result = _product_logic.add_model(fields) 36 # 判斷是否提交成功 37 if result: 38 return web_helper.return_msg(0, '成功') 39 else: 40 return web_helper.return_msg(-1, "提交失敗")
代碼行數比以前是增長了,但可讀性好了不少
版權聲明:本文原創發表於 博客園,做者爲 AllEmpty 本文歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然視爲侵權。
python開發QQ羣:669058475(本羣已滿)、733466321(能夠加2羣) 做者博客:http://www.cnblogs.com/EmptyFS/