幾個複雜的ORM方式都已介紹完了,剩下一些經常使用的刪除、獲取記錄數量、統計合計數、獲取最大值、獲取最小值等方法我就不一一詳細介紹了,直接給出代碼你們自行查看。python
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 select(self, sql): 27 """執行sql查詢語句(select)""" 28 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 29 # 執行sql語句 30 result = db.execute(sql) 31 if not result: 32 result = [] 33 return result 34 35 def execute(self, sql): 36 """執行sql語句,並提交事務""" 37 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 38 # 執行sql語句 39 result = db.execute(sql) 40 if result: 41 db.commit() 42 else: 43 result = [] 44 return result 45 46 def copy(self, values, columns): 47 """批量更新數據""" 48 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 49 # 執行sql語句 50 result = db.copy(values, self.__table_name, columns) 51 return result 52 53 def get_model(self, wheres): 54 """經過條件獲取一條記錄""" 55 # 若是有條件,則自動添加where 56 if wheres: 57 wheres = ' where ' + wheres 58 59 # 合成sql語句 60 sql = "select %(column_name_list)s from %(table_name)s %(wheres)s" % \ 61 {'column_name_list': self.__column_name_list, 'table_name': self.__table_name, 'wheres': wheres} 62 # 初化化數據庫連接 63 result = self.select(sql) 64 if result: 65 return result[0] 66 return {} 67 68 def get_model_for_pk(self, pk, wheres=''): 69 """經過主鍵值獲取數據庫記錄實體""" 70 if not pk: 71 return {} 72 # 組裝查詢條件 73 wheres = '%s = %s' % (self.__pk_name, str(pk)) 74 75 return self.get_model(wheres) 76 77 def get_value(self, column_name, wheres=''): 78 """ 79 獲取指定條件的字段值————多於條記錄時,只取第一條記錄 80 :param column_name: 單個字段名,如:id 81 :param wheres: 查詢條件 82 :return: 7 (指定的字段值) 83 """ 84 if not column_name: 85 return None 86 elif wheres: 87 wheres = ' where ' + wheres 88 89 sql = 'select %(column_name)s from %(table_name)s %(wheres)s limit 1' % \ 90 {'column_name': column_name, 'table_name': self.__table_name, 'wheres': wheres} 91 result = self.select(sql) 92 # 若是查詢成功,則直接返回記錄字典 93 if result: 94 return result[0].get(column_name) 95 96 def get_value_list(self, column_name, wheres=''): 97 """ 98 獲取指定條件記錄的字段值列表 99 :param column_name: 單個字段名,如:id 100 :param wheres: 查詢條件 101 :return: [1,3,4,6,7] 102 """ 103 if not column_name: 104 column_name = self.__pk_name 105 elif wheres: 106 wheres = ' where ' + wheres 107 108 sql = 'select array_agg(%(column_name)s) as list from %(table_name)s %(wheres)s' % \ 109 {'column_name': column_name, 'table_name': self.__table_name, 'wheres': wheres} 110 result = self.select(sql) 111 # 若是查詢失敗或不存在指定條件記錄,則直接返回初始值 112 if result and isinstance(result, list): 113 return result[0].get('list') 114 else: 115 return [] 116 117 def add_model(self, fields, returning=''): 118 """新增數據庫記錄""" 119 ### 拼接sql語句 ### 120 # 初始化變量 121 key_list = [] 122 value_list = [] 123 # 將傳入的字典參數進行處理,把字段名生成sql插入字段名數組和字典替換數組 124 # PS:字符串使用字典替換參數時,格式是%(name)s,這裏會生成對應的字串 125 # 好比: 126 # 傳入的字典爲: {'id': 1, 'name': '名稱'} 127 # 那麼生成的key_list爲:'id','name' 128 # 而value_list爲:'%(id)s,%(name)s' 129 # 最終而value_list爲字符串對應名稱位置會被替換成相應的值 130 for key in fields.keys(): 131 key_list.append(key) 132 value_list.append('%(' + key + ')s') 133 # 設置sql拼接字典,並將數組(lit)使用join方式進行拼接,生成用逗號分隔的字符串 134 parameter = { 135 'table_name': self.__table_name, 136 'pk_name': self.__pk_name, 137 'key_list': ','.join(key_list), 138 'value_list': ','.join(value_list) 139 } 140 # 若是有指定返回參數,則添加 141 if returning: 142 parameter['returning'] = ', ' + returning 143 else: 144 parameter['returning'] = '' 145 146 # 生成可使用字典替換的字符串 147 sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning %(pk_name)s %(returning)s" % parameter 148 # 將生成好的字符串替字典參數值,生成最終可執行的sql語句 149 sql = sql % fields 150 151 result = self.execute(sql) 152 if result: 153 return result[0] 154 return {} 155 156 def edit(self, fields, wheres='', returning=''): 157 """批量編輯數據庫記錄""" 158 ### 拼接sql語句 ### 159 # 拼接字段與值 160 field_list = [key + ' = %(' + key + ')s' for key in fields.keys()] 161 # 設置sql拼接字典 162 parameter = { 163 'table_name': self.__table_name, 164 'pk_name': self.__pk_name, 165 'field_list': ','.join(field_list) 166 } 167 # 若是存在更新條件,則將條件添加到sql拼接更換字典中 168 if wheres: 169 parameter['wheres'] = ' where ' + wheres 170 else: 171 parameter['wheres'] = '' 172 173 # 若是有指定返回參數,則添加 174 if returning: 175 parameter['returning'] = ', ' + returning 176 else: 177 parameter['returning'] = '' 178 179 # 生成sql語句 180 sql = "update %(table_name)s set %(field_list)s %(wheres)s returning %(pk_name)s %(returning)s" % parameter 181 sql = sql % fields 182 183 return self.execute(sql) 184 185 def edit_model(self, pk, fields, wheres='', returning=''): 186 """編輯單條數據庫記錄""" 187 if not pk: 188 return {} 189 elif wheres: 190 wheres = self.__pk_name + ' = ' + str(pk) + ' and ' + wheres 191 else: 192 wheres = self.__pk_name + ' = ' + str(pk) 193 194 return self.edit(fields, wheres, returning) 195 196 def delete(self, wheres='', returning='', is_update_cache=True): 197 """批量刪除數據庫記錄""" 198 # 若是存在條件 199 if wheres: 200 wheres = ' where ' + wheres 201 202 # 若是有指定返回參數,則添加 203 if returning: 204 returning = ', ' + returning 205 206 # 生成sql語句 207 sql = "delete from %(table_name)s %(wheres)s returning %(pk_name)s %(returning)s" % \ 208 {'table_name': self.__table_name, 'wheres': wheres, 'pk_name': self.__pk_name, 'returning': returning} 209 return self.execute(sql) 210 211 def delete_model(self, pk, wheres='', returning='', is_update_cache=True): 212 """刪除單條數據庫記錄""" 213 if not pk: 214 return {} 215 elif wheres: 216 wheres = self.__pk_name + ' = ' + str(pk) + ' and ' + wheres 217 else: 218 wheres = self.__pk_name + ' = ' + str(pk) 219 220 return self.delete(wheres, returning) 221 222 def get_list(self, column_name_list='', wheres='', page_number=None, page_size=None, orderby=None, table_name=None): 223 """ 224 獲取指定條件的數據庫記錄集 225 :param column_name_list: 查詢字段 226 :param wheres: 查詢條件 227 :param page_number: 分頁索引值 228 :param page_size: 分頁大小, 存在值時纔會執行分頁 229 :param orderby: 排序規則 230 :param table_name: 查詢數據表,多表查詢時須要設置 231 :return: 返回記錄集總數量與分頁記錄集 232 {'records': 0, 'total': 0, 'page': 0, 'rows': []} 233 """ 234 # 初始化輸出參數:總記錄數量與列表集 235 data = { 236 'records': 0, # 總記錄數 237 'total': 0, # 總頁數 238 'page': 1, # 當前頁面索引 239 'rows': [], # 查詢結果(記錄列表) 240 } 241 # 初始化查詢數據表名稱 242 if not table_name: 243 table_name = self.__table_name 244 # 初始化查詢字段名 245 if not column_name_list: 246 column_name_list = self.__column_name_list 247 # 初始化查詢條件 248 if wheres: 249 # 若是是字符串,表示該查詢條件已組裝好了,直接可使用 250 if isinstance(wheres, str): 251 wheres = 'where ' + wheres 252 # 若是是list,則表示查詢條件有多個,可使用join將它們用and方式組合起來使用 253 elif isinstance(wheres, list): 254 wheres = 'where ' + ' and '.join(wheres) 255 # 初始化排序 256 if not orderby: 257 orderby = self.__pk_name + ' desc' 258 # 初始化分頁查詢的記錄區間 259 paging = '' 260 261 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 262 ############################################################# 263 # 判斷是否須要進行分頁 264 if not page_size is None: 265 ### 執行sql,獲取指定條件的記錄總數量 266 sql = 'select count(1) as records from %(table_name)s %(wheres)s ' % \ 267 {'table_name': table_name, 'wheres': wheres} 268 result = db.execute(sql) 269 # 若是查詢失敗或不存在指定條件記錄,則直接返回初始值 270 if not result or result[0]['records'] == 0: 271 return data 272 273 # 設置記錄總數量 274 data['records'] = result[0].get('records') 275 276 ######################################################### 277 ### 設置分頁索引與頁面大小 ### 278 if page_size <= 0: 279 page_size = 10 280 # 計算總分頁數量:經過總記錄數除於每頁顯示數量來計算總分頁數量 281 if data['records'] % page_size == 0: 282 page_total = data['records'] // page_size 283 else: 284 page_total = data['records'] // page_size + 1 285 # 判斷頁碼是否超出限制,超出限制查詢時會出現異常,因此將頁面索引設置爲最後一頁 286 if page_number < 1 or page_number > page_total: 287 page_number = page_total 288 # 記錄總頁面數量 289 data['total'] = page_total 290 # 記錄當前頁面值 291 data['page'] = page_number 292 # 計算當前頁面要顯示的記錄起始位置(limit指定的位置) 293 record_number = (page_number - 1) * page_size 294 # 設置查詢分頁條件 295 paging = ' limit ' + str(page_size) + ' offset ' + str(record_number) 296 ############################################################# 297 298 ### 按條件查詢數據庫記錄 299 sql = "select %(column_name_list)s from %(table_name)s %(wheres)s order by %(orderby)s %(paging)s" % \ 300 {'column_name_list': column_name_list, 301 'table_name': table_name, 302 'wheres': wheres, 303 'orderby': orderby, 304 'paging': paging} 305 result = db.execute(sql) 306 if result: 307 data['rows'] = result 308 # 不須要分頁查詢時,直接在這裏設置總記錄數 309 if page_size is None: 310 data['records'] = len(result) 311 312 return data 313 314 def get_count(self, wheres=''): 315 """獲取指定條件記錄數量""" 316 if wheres: 317 wheres = ' where ' + wheres 318 sql = 'select count(1) as total from %(table_name)s %(wheres)s ' % \ 319 {'table_name': self.__table_name, 'wheres': wheres} 320 result = self.select(sql) 321 # 若是查詢存在記錄,則返回true 322 if result: 323 return result[0].get('total') 324 return 0 325 326 def get_sum(self, fields, wheres): 327 """獲取指定條件記錄數量""" 328 sql = 'select sum(%(fields)s) as total from %(table_name)s where %(wheres)s ' % \ 329 {'table_name': self.__table_name, 'wheres': wheres, 'fields': fields} 330 result = self.select(sql) 331 # 若是查詢存在記錄,則返回true 332 if result and result[0].get('total'): 333 return result[0].get('total') 334 return 0 335 336 def get_min(self, fields, wheres): 337 """獲取該列記錄最小值""" 338 sql = 'select min(%(fields)s) as min from %(table_name)s where %(wheres)s ' % \ 339 {'table_name': self.__table_name, 'wheres': wheres, 'fields': fields} 340 result = self.select(sql) 341 # 若是查詢存在記錄,則返回true 342 if result and result[0].get('min'): 343 return result[0].get('min') 344 345 def get_max(self, fields, wheres): 346 """獲取該列記錄最大值""" 347 sql = 'select max(%(fields)s) as max from %(table_name)s where %(wheres)s ' % \ 348 {'table_name': self.__table_name, 'wheres': wheres, 'fields': fields} 349 result = self.select(sql) 350 # 若是查詢存在記錄,則返回true 351 if result and result[0].get('max'): 352 return result[0].get('max') 353 354 #####################################################################
你們只要掌握了ORM簡單的組合sql方法,就能夠自由發揮,根據本身的須要去建立不一樣的方法了,也能夠隨意更換mysql、mssql等數據庫。mysql
固然,這只是最簡單的ORM方式,提交字段參數和條件參數時,它不會自動分辨字段的類型,不會自動初始化默認值,若是想讓它變的更增強大,還須要作更多的改造與處理,這樣作的話它也會跟着變的更加複雜和難懂,性能也會跟着降低。不過當前功能對於多數項目來講,已經足夠使用了。你們若是有須要能夠自行研究進行擴展。redis
在平常操做中,獲取指定記錄實體是最多見使用最頻繁的操做,爲了減小對數據庫的查詢,咱們能夠將ORM與Nosql結合起來,提高ORM的操做性能,固然若是你不想使用nosql緩存,也能夠直接跳過本章節。sql
使用Nosql,首先咱們須要一個連接Nosql的配置文件,用它來存儲Nosql的服務地址、端口、密碼等參數數據庫
在config文件夾中咱們建立redis_config.py配置文件json
#!/usr/bin/env python # coding=utf-8 ### redis緩存配置參數 ### REDIS = { # 服務地址 'server': '127.0.0.1', # 服務端口 'post': 6379, # 服務密碼 'pwd': '', # 數據庫序號 'db': 1 }
而後咱們還須要一個nosql連接工具包(cache_helper.py),用來對nosql進行set、get、delete和clear操做(存儲、獲取、刪除和清空緩存)數組
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 import redis 5 6 from common import log_helper 7 from config import redis_config 8 9 # 設置redis配置參數 10 _redis = redis_config.REDIS 11 # 初始化Redis緩存連接 12 r = None 13 try: 14 if not r: 15 r = redis.Redis(host=_redis.get('server', ''), 16 port=_redis.get('post', ''), 17 db=_redis.get('db', ''), 18 password=_redis.get('pwd', ''), 19 socket_timeout=1, 20 socket_connect_timeout=1) 21 except Exception as e: 22 log_helper.info('鏈接redis出錯:(' + str(_redis) + ')' + str(e.args)) 23 pass 24 25 26 def set(key, value, time=86400): 27 """ 28 寫緩存 29 :param key: 緩存key,字符串,不區分大小寫 30 :param value: 要存儲的值 31 :param time: 緩存過時時間(單位:秒),0=永不過時 32 :return: 33 """ 34 # 將key轉換爲小寫字母 35 key = str(key).lower() 36 try: 37 r.set(key, value, time) 38 except Exception as e: 39 log_helper.info('寫緩存失敗:key(' + key + ')' + str(e.args)) 40 pass 41 42 43 def get(key): 44 """ 45 讀緩存 46 :param key: 緩存key,字符串,不區分大小寫 47 :return: 48 """ 49 # 將key轉換爲小寫字母 50 key = str(key).lower() 51 try: 52 value = r.get(key) 53 except Exception as e: 54 # log_helper.error('讀緩存失敗:key(' + key + ')' + str(e.args) + ' r:' + str(r) + ' _redis:' + str(_redis)) 55 value = None 56 57 return _str_to_json(value) 58 59 60 def push(key, value): 61 """ 62 添加數據到隊列頭部 63 :param key: 緩存key,字符串,不區分大小寫 64 :param value: 要存儲的值 65 """ 66 # 將key轉換爲小寫字母 67 key = str(key).lower() 68 try: 69 r.lpush(key, value) 70 except Exception as e: 71 log_helper.info('寫緩存失敗:key(' + key + ')' + str(e.args)) 72 pass 73 74 75 def pop(key): 76 """ 77 從緩存隊列的後尾讀取一條數據 78 :param key: 緩存key,字符串,不區分大小寫 79 :return: 緩存數據 80 """ 81 # 將key轉換爲小寫字母 82 key = str(key).lower() 83 try: 84 value = r.rpop(key) 85 except Exception as e: 86 log_helper.info('讀取緩存隊列失敗:key(' + key + ')' + str(e.args)) 87 value = None 88 89 return _str_to_json(value) 90 91 92 def _str_to_json(value): 93 """ 94 將緩存中讀取出來的字符串轉換成對應的數據、元組、列表或字典 95 """ 96 if not value: 97 return value 98 # 不然直接轉換 99 try: 100 value = value.decode() 101 return eval(value) 102 except Exception as e: 103 print(e.args) 104 pass 105 # 不然直接輸出字符串 106 return value 107 108 109 def delete(key): 110 """ 111 刪除緩存 112 :param key:緩存key,字符串,不區分大小寫 113 :return: 114 """ 115 # 將key轉換爲小寫字母 116 key = str(key).lower() 117 try: 118 log_helper.info(str(r.delete(key))) 119 except Exception as e: 120 log_helper.info('Exception:' + str(e.args)) 121 pass 122 123 124 def clear(): 125 """ 126 清空全部緩存 127 """ 128 try: 129 r.flushdb() 130 except: 131 pass
我經常使用的是redis,因此使用cache_helper.py時,須要安裝redis服務和對應的Python包。若是你使用的是memcache,你只須要重構一下cache_helper.py代碼就能夠了。緩存
接下來咱們改造一下邏輯層基類(ORM模塊)app
首先咱們須要導入cache_helpernosql
from common import db_helper, cache_helper
在使用nosql緩存時,你們都知道咱們是使用key來進行對象存取的,而這個key也是惟一的,因此key的生成就很重要的,爲了不key的重複,咱們在對記錄設置key時,能夠用表名+主鍵id的方式來組合key,固然爲了調用方便,能夠將獲取key寫成一個方法來生成
def get_cache_key(self, pk): """獲取緩存key值""" return ''.join((self.__table_name, '_', str(pk)))
這裏使用join的方法,將表名、下橫線、主鍵值組合生成緩存key字符串
對於緩存的操做,主要有設置緩存、獲取緩存、刪除緩存這三種操做,固然爲了方便咱們獲取記錄中指定字段值,咱們能夠增長讀取指定字段值方法。
首先是設置緩存方法,你們看看下面代碼,它很是簡單,先調用生成緩存key,而後將對象存儲到緩存中,並指定過時時間,當設置time爲0時,它將永不過時
def set_model_for_cache(self, pk, value, time=43200): """更新存儲在緩存中的數據庫記錄,緩存過時時間爲12小時""" # 生成緩存key key = self.get_cache_key(pk) # 存儲到nosql緩存中 cache_helper.set(key, value, time)
接着是獲取緩存對象方法
def get_model_for_cache(self, pk): """從緩存中讀取數據庫記錄""" # 生成緩存key key = self.get_cache_key(pk) # 從緩存中讀取數據庫記錄 result = cache_helper.get(key) # 緩存中不存在記錄,則從數據庫獲取 if not result: result = self.get_model_for_pk(pk) self.set_model_for_cache(pk, result) if result: return result else: return {}
咱們首先要作的一樣是生成緩存key,而後調用get方法從緩存中讀取對象,執行完後,須要判斷該對象是否存在緩存中,若是不存在則表示該對象並未存儲到緩存中或它可能存儲過時了,因此須要從新從數據庫中讀取出來,並將它存儲到緩存中,而後將讀取出來的記錄實體返回出去。
而後咱們再增長一個讀取指定記錄字段值的方法
def get_value_for_cache(self, pk, column_name): """獲取指定記錄的字段值""" return self.get_model_for_cache(pk).get(column_name)
它直接調用獲取緩存對象方法,而後從返回的對象中讀取指定的字段值就能夠了
刪除緩存方法也很簡單,生成緩存key後,直接調用delete進行刪除。對於刪除方法,有時候調用不知是否是nosql自身bug問題,仍是在主從關係的nosql中讀寫分離會引發刪除失敗,若是出現這種狀況,能夠將delete改成set,將該緩存set爲空就能夠解決這個問題
def del_model_for_cache(self, pk): """刪除緩存中指定數據""" # 生成緩存key key = self.get_cache_key(pk) # log_helper.info(key) # 存儲到nosql緩存中 cache_helper.delete(key)
PS:在使用緩存操做時,有時咱們直接對數據庫進行操做,就會引發數據與緩存不匹配,出現髒數據的狀況,這時在後臺增長清空緩存的操做,直接調用cache_helper.clear()進行清空緩存。
基本方法都完成了,接下來就是要對ORM的刪除與修改方法進行改造了,讓它們自行根據須要對緩存進行對應操做,讓緩存與數據表中的記錄保持一致。
在改造時,咱們只須要對刪除與修改操做進行處理,對新增與查詢操做不須要操做,由於新增的記錄,它並在緩存中並不存在,因此不須要進行操做,而查詢也不會改變數據內容,只有進行刪除和修改操做時,纔會變更數據內容,這時就須要更改緩存,讓數據保持一致。
改造編輯記錄實體方法
1 def edit(self, fields, wheres='', returning='', is_update_cache=True): 2 """ 3 批量編輯數據庫記錄 4 :param fields: 要更新的字段(字段名與值存儲在字典中) 5 :param wheres: 更新條件 6 :param returning: 更新成功後,返回的字段名 7 :param is_update_cache: 是否同步更新緩存 8 :return: 9 """ 10 ### 拼接sql語句 ### 11 # 拼接字段與值 12 field_list = [key + ' = %(' + key + ')s' for key in fields.keys()] 13 # 設置sql拼接字典 14 parameter = { 15 'table_name': self.__table_name, 16 'pk_name': self.__pk_name, 17 'field_list': ','.join(field_list) 18 } 19 # 若是存在更新條件,則將條件添加到sql拼接更換字典中 20 if wheres: 21 parameter['wheres'] = ' where ' + wheres 22 else: 23 parameter['wheres'] = '' 24 25 # 若是有指定返回參數,則添加 26 if returning: 27 parameter['returning'] = ', ' + returning 28 else: 29 parameter['returning'] = '' 30 31 # 生成sql語句 32 sql = "update %(table_name)s set %(field_list)s %(wheres)s returning %(pk_name)s %(returning)s" % parameter 33 sql = sql % fields 34 35 result = self.execute(sql) 36 if result: 37 # 判斷是否刪除對應的緩存 38 if is_update_cache: 39 # 循環刪除更新成功的全部記錄對應的緩存 40 for model in result: 41 self.del_model_for_cache(model.get('id', 0)) 42 return result
你們能夠看到,該方法增長了is_update_cache 是否同步更新緩存參數,這是由於咱們在使用緩存時會存在一些特殊狀況,好比說批量更新不少數據時,若是使用循環逐條清理對應緩存時,會佔用較多資源,咱們能夠關掉緩存的同步更新,直接調用clear清空全部緩存會更加快捷;又好比說,頁面訪問數的更新,它會更新的很是頻繁,咱們不須要實時清除,可使用其餘方式觸發清理,也能夠將點擊數用獨立緩存存儲使用等
而清理緩存,咱們只須要將緩存內容直接刪除就能夠了,由於執行更新之後,返回的記錄實體沒有設置爲*時,只返回主鍵id,直接設置的話會形成緩存數據丟失細節的問題,另外,咱們執行更新之後,該記錄也不必定還會被讀取出來。
刪除記錄也進行同樣的改造
1 def delete(self, wheres='', returning='', is_update_cache=True): 2 """ 3 批量刪除數據庫記錄 4 :param wheres: 刪除條件 5 :param returning: 刪除成功後,返回的字段名 6 :param is_update_cache: 是否同步更新緩存 7 :return: 8 """ 9 # 若是存在條件 10 if wheres: 11 wheres = ' where ' + wheres 12 13 # 若是有指定返回參數,則添加 14 if returning: 15 returning = ', ' + returning 16 17 # 生成sql語句 18 sql = "delete from %(table_name)s %(wheres)s returning %(pk_name)s %(returning)s" % \ 19 {'table_name': self.__table_name, 'wheres': wheres, 'pk_name': self.__pk_name, 'returning': returning} 20 result = self.execute(sql) 21 if result: 22 # 同步刪除對應的緩存 23 if is_update_cache: 24 for model in result: 25 self.del_model_for_cache(model.get('id', 0)) 26 return result
對於緩存基本上就這兩個要進行改造的操做了。在實現開發中,咱們認真想想,其實咱們還會存在一些特殊的狀況,好比說咱們對數據進行加工處理後,將加工後的值存儲到緩存中,而對相關記錄進行修改或刪除操做之後,因爲這些緩存它與記錄並無關聯,因此執行相關操做之後,它就變成孤島,不會實時同步,產生髒數據。因此咱們須要有一個功能,能夠將它們管理起來,與該數據表的修改和刪除操做關聯起來,進行修改和刪除操做後同步清除這些特殊緩存。
根據這些要求,咱們就須要再增長兩個緩存操做方法,用來存儲這些特殊的緩存名稱,而後在進行修改和刪除操做時,同步清除這些特殊緩存。
首先咱們須要在初始化方法中,添加一個綁定該數據表的全局緩存變量self.__cache_list,它由表名稱+_cache_list組成。
1 def __init__(self, db, is_output_sql, table_name, column_name_list='*', pk_name='id'): 2 """類初始化""" 3 # 數據庫參數 4 self.__db = db 5 # 是否輸出執行的Sql語句到日誌中 6 self.__is_output_sql = is_output_sql 7 # 表名稱 8 self.__table_name = str(table_name).lower() 9 # 查詢的列字段名稱,*表示查詢所有字段,多於1個字段時用逗號進行分隔,除了字段名外,也能夠是表達式 10 self.__column_name_list = str(column_name_list).lower() 11 # 主健名稱 12 self.__pk_name = str(pk_name).lower() 13 # 緩存列表 14 self.__cache_list = self.__table_name + '_cache_list'
而後咱們再添加特殊緩存存儲方法
1 def add_relevance_cache_in_list(self, key): 2 """將緩存名稱存儲到列表裏————主要存儲與記錄變動關聯的""" 3 # 從nosql中讀取全局緩存列表 4 cache_list = cache_helper.get(self.__cache_list) 5 # 判斷緩存列表是否有值,有則進行添加操做 6 if cache_list: 7 # 判斷是否已存儲列表中,不存在則執行添加操做 8 if not key in cache_list: 9 cache_list.append(key) 10 cache_helper.set(self.__cache_list, cache_list) 11 # 無則直接建立全局緩存列表,並存儲到nosql中 12 else: 13 cache_list = [key] 14 cache_helper.set(self.__cache_list, cache_list)
執行該方法,會將咱們自定義的緩存名稱存儲到全局緩存變量中
接着咱們再添加一個清除全部特殊緩存的方法
1 def del_relevance_cache(self): 2 """刪除關聯緩存————將和數據表記錄關聯的,個性化緩存所有刪除""" 3 # 從nosql中讀取全局緩存列表 4 cache_list = cache_helper.get(self.__cache_list) 5 # 清除已刪除緩存列表 6 cache_helper.delete(self.__cache_list) 7 if cache_list: 8 # 執行刪除操做 9 for cache in cache_list: 10 cache_helper.delete(cache)
添加完成之後,咱們再來改造一下修改與刪除代碼,只須要在裏面添加清除全部特殊緩存方法就能夠了
1 def edit(self, fields, wheres='', returning='', is_update_cache=True): 2 """ 3 批量編輯數據庫記錄 4 :param fields: 要更新的字段(字段名與值存儲在字典中) 5 :param wheres: 更新條件 6 :param returning: 更新成功後,返回的字段名 7 :param is_update_cache: 是否同步更新緩存 8 :return: 9 """ 10 ### 拼接sql語句 ### 11 # 拼接字段與值 12 field_list = [key + ' = %(' + key + ')s' for key in fields.keys()] 13 # 設置sql拼接字典 14 parameter = { 15 'table_name': self.__table_name, 16 'pk_name': self.__pk_name, 17 'field_list': ','.join(field_list) 18 } 19 # 若是存在更新條件,則將條件添加到sql拼接更換字典中 20 if wheres: 21 parameter['wheres'] = ' where ' + wheres 22 else: 23 parameter['wheres'] = '' 24 25 # 若是有指定返回參數,則添加 26 if returning: 27 parameter['returning'] = ', ' + returning 28 else: 29 parameter['returning'] = '' 30 31 # 生成sql語句 32 sql = "update %(table_name)s set %(field_list)s %(wheres)s returning %(pk_name)s %(returning)s" % parameter 33 sql = sql % fields 34 35 result = self.execute(sql) 36 if result: 37 # 判斷是否刪除對應的緩存 38 if is_update_cache: 39 # 循環刪除更新成功的全部記錄對應的緩存 40 for model in result: 41 self.del_model_for_cache(model.get('id', 0)) 42 # 同步刪除與本表關聯的緩存 43 self.del_relevance_cache() 44 return result 45 46 def delete(self, wheres='', returning='', is_update_cache=True): 47 """ 48 批量刪除數據庫記錄 49 :param wheres: 刪除條件 50 :param returning: 刪除成功後,返回的字段名 51 :param is_update_cache: 是否同步更新緩存 52 :return: 53 """ 54 # 若是存在條件 55 if wheres: 56 wheres = ' where ' + wheres 57 58 # 若是有指定返回參數,則添加 59 if returning: 60 returning = ', ' + returning 61 62 # 生成sql語句 63 sql = "delete from %(table_name)s %(wheres)s returning %(pk_name)s %(returning)s" % \ 64 {'table_name': self.__table_name, 'wheres': wheres, 'pk_name': self.__pk_name, 'returning': returning} 65 result = self.execute(sql) 66 if result: 67 # 同步刪除對應的緩存 68 if is_update_cache: 69 for model in result: 70 self.del_model_for_cache(model.get('id', 0)) 71 # 同步刪除與本表關聯的緩存 72 self.del_relevance_cache() 73 return result
ORM的緩存改造就所有完成了,下面是完整代碼
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 from common import db_helper, cache_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 self.__cache_list = self.__table_name + '_cache_list' 24 25 ##################################################################### 26 ### 執行Sql ### 27 28 def select(self, sql): 29 """執行sql查詢語句(select)""" 30 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 31 # 執行sql語句 32 result = db.execute(sql) 33 if not result: 34 result = [] 35 return result 36 37 def execute(self, sql): 38 """執行sql語句,並提交事務""" 39 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 40 # 執行sql語句 41 result = db.execute(sql) 42 if result: 43 db.commit() 44 else: 45 result = [] 46 return result 47 48 def copy(self, values, columns): 49 """批量更新數據""" 50 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 51 # 執行sql語句 52 result = db.copy(values, self.__table_name, columns) 53 return result 54 55 def get_model(self, wheres): 56 """經過條件獲取一條記錄""" 57 # 若是有條件,則自動添加where 58 if wheres: 59 wheres = ' where ' + wheres 60 61 # 合成sql語句 62 sql = "select %(column_name_list)s from %(table_name)s %(wheres)s" % \ 63 {'column_name_list': self.__column_name_list, 'table_name': self.__table_name, 'wheres': wheres} 64 # 初化化數據庫連接 65 result = self.select(sql) 66 if result: 67 return result[0] 68 return {} 69 70 def get_model_for_pk(self, pk, wheres=''): 71 """經過主鍵值獲取數據庫記錄實體""" 72 if not pk: 73 return {} 74 # 組裝查詢條件 75 wheres = '%s = %s' % (self.__pk_name, str(pk)) 76 77 return self.get_model(wheres) 78 79 def get_value(self, column_name, wheres=''): 80 """ 81 獲取指定條件的字段值————多於條記錄時,只取第一條記錄 82 :param column_name: 單個字段名,如:id 83 :param wheres: 查詢條件 84 :return: 7 (指定的字段值) 85 """ 86 if not column_name: 87 return None 88 elif wheres: 89 wheres = ' where ' + wheres 90 91 sql = 'select %(column_name)s from %(table_name)s %(wheres)s limit 1' % \ 92 {'column_name': column_name, 'table_name': self.__table_name, 'wheres': wheres} 93 result = self.select(sql) 94 # 若是查詢成功,則直接返回記錄字典 95 if result: 96 return result[0].get(column_name) 97 98 def get_value_list(self, column_name, wheres=''): 99 """ 100 獲取指定條件記錄的字段值列表 101 :param column_name: 單個字段名,如:id 102 :param wheres: 查詢條件 103 :return: [1,3,4,6,7] 104 """ 105 if not column_name: 106 column_name = self.__pk_name 107 elif wheres: 108 wheres = ' where ' + wheres 109 110 sql = 'select array_agg(%(column_name)s) as list from %(table_name)s %(wheres)s' % \ 111 {'column_name': column_name, 'table_name': self.__table_name, 'wheres': wheres} 112 result = self.select(sql) 113 # 若是查詢失敗或不存在指定條件記錄,則直接返回初始值 114 if result and isinstance(result, list): 115 return result[0].get('list') 116 else: 117 return [] 118 119 def add_model(self, fields, returning=''): 120 """新增數據庫記錄""" 121 ### 拼接sql語句 ### 122 # 初始化變量 123 key_list = [] 124 value_list = [] 125 # 將傳入的字典參數進行處理,把字段名生成sql插入字段名數組和字典替換數組 126 # PS:字符串使用字典替換參數時,格式是%(name)s,這裏會生成對應的字串 127 # 好比: 128 # 傳入的字典爲: {'id': 1, 'name': '名稱'} 129 # 那麼生成的key_list爲:'id','name' 130 # 而value_list爲:'%(id)s,%(name)s' 131 # 最終而value_list爲字符串對應名稱位置會被替換成相應的值 132 for key in fields.keys(): 133 key_list.append(key) 134 value_list.append('%(' + key + ')s') 135 # 設置sql拼接字典,並將數組(lit)使用join方式進行拼接,生成用逗號分隔的字符串 136 parameter = { 137 'table_name': self.__table_name, 138 'pk_name': self.__pk_name, 139 'key_list': ','.join(key_list), 140 'value_list': ','.join(value_list) 141 } 142 # 若是有指定返回參數,則添加 143 if returning: 144 parameter['returning'] = ', ' + returning 145 else: 146 parameter['returning'] = '' 147 148 # 生成可使用字典替換的字符串 149 sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning %(pk_name)s %(returning)s" % parameter 150 # 將生成好的字符串替字典參數值,生成最終可執行的sql語句 151 sql = sql % fields 152 153 result = self.execute(sql) 154 if result: 155 return result[0] 156 return {} 157 158 def edit(self, fields, wheres='', returning='', is_update_cache=True): 159 """ 160 批量編輯數據庫記錄 161 :param fields: 要更新的字段(字段名與值存儲在字典中) 162 :param wheres: 更新條件 163 :param returning: 更新成功後,返回的字段名 164 :param is_update_cache: 是否同步更新緩存 165 :return: 166 """ 167 ### 拼接sql語句 ### 168 # 拼接字段與值 169 field_list = [key + ' = %(' + key + ')s' for key in fields.keys()] 170 # 設置sql拼接字典 171 parameter = { 172 'table_name': self.__table_name, 173 'pk_name': self.__pk_name, 174 'field_list': ','.join(field_list) 175 } 176 # 若是存在更新條件,則將條件添加到sql拼接更換字典中 177 if wheres: 178 parameter['wheres'] = ' where ' + wheres 179 else: 180 parameter['wheres'] = '' 181 182 # 若是有指定返回參數,則添加 183 if returning: 184 parameter['returning'] = ', ' + returning 185 else: 186 parameter['returning'] = '' 187 188 # 生成sql語句 189 sql = "update %(table_name)s set %(field_list)s %(wheres)s returning %(pk_name)s %(returning)s" % parameter 190 sql = sql % fields 191 192 result = self.execute(sql) 193 if result: 194 # 判斷是否刪除對應的緩存 195 if is_update_cache: 196 # 循環刪除更新成功的全部記錄對應的緩存 197 for model in result: 198 self.del_model_for_cache(model.get('id', 0)) 199 # 同步刪除與本表關聯的緩存 200 self.del_relevance_cache() 201 return result 202 203 def edit_model(self, pk, fields, wheres='', returning=''): 204 """編輯單條數據庫記錄""" 205 if not pk: 206 return {} 207 elif wheres: 208 wheres = self.__pk_name + ' = ' + str(pk) + ' and ' + wheres 209 else: 210 wheres = self.__pk_name + ' = ' + str(pk) 211 212 return self.edit(fields, wheres, returning) 213 214 def delete(self, wheres='', returning='', is_update_cache=True): 215 """ 216 批量刪除數據庫記錄 217 :param wheres: 刪除條件 218 :param returning: 刪除成功後,返回的字段名 219 :param is_update_cache: 是否同步更新緩存 220 :return: 221 """ 222 # 若是存在條件 223 if wheres: 224 wheres = ' where ' + wheres 225 226 # 若是有指定返回參數,則添加 227 if returning: 228 returning = ', ' + returning 229 230 # 生成sql語句 231 sql = "delete from %(table_name)s %(wheres)s returning %(pk_name)s %(returning)s" % \ 232 {'table_name': self.__table_name, 'wheres': wheres, 'pk_name': self.__pk_name, 'returning': returning} 233 result = self.execute(sql) 234 if result: 235 # 同步刪除對應的緩存 236 if is_update_cache: 237 for model in result: 238 self.del_model_for_cache(model.get('id', 0)) 239 # 同步刪除與本表關聯的緩存 240 self.del_relevance_cache() 241 return result 242 243 def delete_model(self, pk, wheres='', returning='', is_update_cache=True): 244 """刪除單條數據庫記錄""" 245 if not pk: 246 return {} 247 elif wheres: 248 wheres = self.__pk_name + ' = ' + str(pk) + ' and ' + wheres 249 else: 250 wheres = self.__pk_name + ' = ' + str(pk) 251 252 return self.delete(wheres, returning) 253 254 def get_list(self, column_name_list='', wheres='', page_number=None, page_size=None, orderby=None, table_name=None): 255 """ 256 獲取指定條件的數據庫記錄集 257 :param column_name_list: 查詢字段 258 :param wheres: 查詢條件 259 :param page_number: 分頁索引值 260 :param page_size: 分頁大小, 存在值時纔會執行分頁 261 :param orderby: 排序規則 262 :param table_name: 查詢數據表,多表查詢時須要設置 263 :return: 返回記錄集總數量與分頁記錄集 264 {'records': 0, 'total': 0, 'page': 0, 'rows': []} 265 """ 266 # 初始化輸出參數:總記錄數量與列表集 267 data = { 268 'records': 0, # 總記錄數 269 'total': 0, # 總頁數 270 'page': 1, # 當前頁面索引 271 'rows': [], # 查詢結果(記錄列表) 272 } 273 # 初始化查詢數據表名稱 274 if not table_name: 275 table_name = self.__table_name 276 # 初始化查詢字段名 277 if not column_name_list: 278 column_name_list = self.__column_name_list 279 # 初始化查詢條件 280 if wheres: 281 # 若是是字符串,表示該查詢條件已組裝好了,直接可使用 282 if isinstance(wheres, str): 283 wheres = 'where ' + wheres 284 # 若是是list,則表示查詢條件有多個,可使用join將它們用and方式組合起來使用 285 elif isinstance(wheres, list): 286 wheres = 'where ' + ' and '.join(wheres) 287 # 初始化排序 288 if not orderby: 289 orderby = self.__pk_name + ' desc' 290 # 初始化分頁查詢的記錄區間 291 paging = '' 292 293 with db_helper.PgHelper(self.__db, self.__is_output_sql) as db: 294 ############################################################# 295 # 判斷是否須要進行分頁 296 if not page_size is None: 297 ### 執行sql,獲取指定條件的記錄總數量 298 sql = 'select count(1) as records from %(table_name)s %(wheres)s ' % \ 299 {'table_name': table_name, 'wheres': wheres} 300 result = db.execute(sql) 301 # 若是查詢失敗或不存在指定條件記錄,則直接返回初始值 302 if not result or result[0]['records'] == 0: 303 return data 304 305 # 設置記錄總數量 306 data['records'] = result[0].get('records') 307 308 ######################################################### 309 ### 設置分頁索引與頁面大小 ### 310 if page_size <= 0: 311 page_size = 10 312 # 計算總分頁數量:經過總記錄數除於每頁顯示數量來計算總分頁數量 313 if data['records'] % page_size == 0: 314 page_total = data['records'] // page_size 315 else: 316 page_total = data['records'] // page_size + 1 317 # 判斷頁碼是否超出限制,超出限制查詢時會出現異常,因此將頁面索引設置爲最後一頁 318 if page_number < 1 or page_number > page_total: 319 page_number = page_total 320 # 記錄總頁面數量 321 data['total'] = page_total 322 # 記錄當前頁面值 323 data['page'] = page_number 324 # 計算當前頁面要顯示的記錄起始位置(limit指定的位置) 325 record_number = (page_number - 1) * page_size 326 # 設置查詢分頁條件 327 paging = ' limit ' + str(page_size) + ' offset ' + str(record_number) 328 ############################################################# 329 330 ### 按條件查詢數據庫記錄 331 sql = "select %(column_name_list)s from %(table_name)s %(wheres)s order by %(orderby)s %(paging)s" % \ 332 {'column_name_list': column_name_list, 333 'table_name': table_name, 334 'wheres': wheres, 335 'orderby': orderby, 336 'paging': paging} 337 result = db.execute(sql) 338 if result: 339 data['rows'] = result 340 # 不須要分頁查詢時,直接在這裏設置總記錄數 341 if page_size is None: 342 data['records'] = len(result) 343 344 return data 345 346 def get_count(self, wheres=''): 347 """獲取指定條件記錄數量""" 348 if wheres: 349 wheres = ' where ' + wheres 350 sql = 'select count(1) as total from %(table_name)s %(wheres)s ' % \ 351 {'table_name': self.__table_name, 'wheres': wheres} 352 result = self.select(sql) 353 # 若是查詢存在記錄,則返回true 354 if result: 355 return result[0].get('total') 356 return 0 357 358 def get_sum(self, fields, wheres): 359 """獲取指定條件記錄數量""" 360 sql = 'select sum(%(fields)s) as total from %(table_name)s where %(wheres)s ' % \ 361 {'table_name': self.__table_name, 'wheres': wheres, 'fields': fields} 362 result = self.select(sql) 363 # 若是查詢存在記錄,則返回true 364 if result and result[0].get('total'): 365 return result[0].get('total') 366 return 0 367 368 def get_min(self, fields, wheres): 369 """獲取該列記錄最小值""" 370 sql = 'select min(%(fields)s) as min from %(table_name)s where %(wheres)s ' % \ 371 {'table_name': self.__table_name, 'wheres': wheres, 'fields': fields} 372 result = self.select(sql) 373 # 若是查詢存在記錄,則返回true 374 if result and result[0].get('min'): 375 return result[0].get('min') 376 377 def get_max(self, fields, wheres): 378 """獲取該列記錄最大值""" 379 sql = 'select max(%(fields)s) as max from %(table_name)s where %(wheres)s ' % \ 380 {'table_name': self.__table_name, 'wheres': wheres, 'fields': fields} 381 result = self.select(sql) 382 # 若是查詢存在記錄,則返回true 383 if result and result[0].get('max'): 384 return result[0].get('max') 385 386 ##################################################################### 387 388 389 ##################################################################### 390 ### 緩存操做方法 ### 391 392 def get_cache_key(self, pk): 393 """獲取緩存key值""" 394 return ''.join((self.__table_name, '_', str(pk))) 395 396 def set_model_for_cache(self, pk, value, time=43200): 397 """更新存儲在緩存中的數據庫記錄,緩存過時時間爲12小時""" 398 # 生成緩存key 399 key = self.get_cache_key(pk) 400 # 存儲到nosql緩存中 401 cache_helper.set(key, value, time) 402 403 def get_model_for_cache(self, pk): 404 """從緩存中讀取數據庫記錄""" 405 # 生成緩存key 406 key = self.get_cache_key(pk) 407 # 從緩存中讀取數據庫記錄 408 result = cache_helper.get(key) 409 # 緩存中不存在記錄,則從數據庫獲取 410 if not result: 411 result = self.get_model_for_pk(pk) 412 self.set_model_for_cache(pk, result) 413 if result: 414 return result 415 else: 416 return {} 417 418 def get_value_for_cache(self, pk, column_name): 419 """獲取指定記錄的字段值""" 420 return self.get_model_for_cache(pk).get(column_name) 421 422 def del_model_for_cache(self, pk): 423 """刪除緩存中指定數據""" 424 # 生成緩存key 425 key = self.get_cache_key(pk) 426 # log_helper.info(key) 427 # 存儲到nosql緩存中 428 cache_helper.delete(key) 429 430 def add_relevance_cache_in_list(self, key): 431 """將緩存名稱存儲到列表裏————主要存儲與記錄變動關聯的""" 432 # 從nosql中讀取全局緩存列表 433 cache_list = cache_helper.get(self.__cache_list) 434 # 判斷緩存列表是否有值,有則進行添加操做 435 if cache_list: 436 # 判斷是否已存儲列表中,不存在則執行添加操做 437 if not key in cache_list: 438 cache_list.append(key) 439 cache_helper.set(self.__cache_list, cache_list) 440 # 無則直接建立全局緩存列表,並存儲到nosql中 441 else: 442 cache_list = [key] 443 cache_helper.set(self.__cache_list, cache_list) 444 445 def del_relevance_cache(self): 446 """刪除關聯緩存————將和數據表記錄關聯的,個性化緩存所有刪除""" 447 # 從nosql中讀取全局緩存列表 448 cache_list = cache_helper.get(self.__cache_list) 449 # 清除已刪除緩存列表 450 cache_helper.delete(self.__cache_list) 451 if cache_list: 452 # 執行刪除操做 453 for cache in cache_list: 454 cache_helper.delete(cache) 455 456 #####################################################################
版權聲明:本文原創發表於 博客園,做者爲 AllEmpty 本文歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然視爲侵權。
python開發QQ羣:669058475(本羣已滿)、733466321(能夠加2羣) 做者博客:http://www.cnblogs.com/EmptyFS/