我的第一个python web开发框架(31)——定制ORM(七)
几个复杂的ORM方式都已介绍完了,剩下一些常用的删除、获取记录数量、统计合计数、获取最大值、获取最小值等方法我就不一一详细介绍了,直接给出代码大家自行查看。
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等数据库。
当然,这只是最简单的ORM方式,提交字段参数和条件参数时,它不会自动分辨字段的类型,不会自动初始化默认值,如果想让它变的更加强大,还需要做更多的改造与处理,这样做的话它也会跟着变的更加复杂和难懂,性能也会跟着下降。不过当前功能对于多数项目来说,已经足够使用了。大家如果有需要可以自行研究进行扩展。
在日常操作中,获取指定记录实体是最常见使用最频繁的操作,为了减少对数据库的查询,我们可以将ORM与Nosql结合起来,提升ORM的操作性能,当然如果你不想使用nosql缓存,也可以直接跳过本章节。
使用Nosql,首先我们需要一个链接Nosql的配置文件,用它来存储Nosql的服务地址、端口、密码等参数
在config文件夹中我们创建redis_config.py配置文件
#!/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模块)
首先我们需要导入cache_helper
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/