几个复杂的ORM方式都已介绍完了,剩下一些常用的删除、获取记录数量、统计合计数、获取最大值、获取最小值等方法我就不一一详细介绍了,直接给出代码大家自行查看。
1 #!/usr/bin/env python
2 #coding=utf-8
3
4 from common importdb_helper5
6
7 classLogicBase():8 """逻辑层基础类"""
9
10 def __init__(self, db, is_output_sql, table_name, column_name_list='*', pk_name='id'):11 """类初始化"""
12 #数据库参数
13 self.__db =db14 #是否输出执行的Sql语句到日志中
15 self.__is_output_sql =is_output_sql16 #表名称
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 defselect(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 notresult:32 result =[]33 returnresult34
35 defexecute(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 ifresult:41 db.commit()42 else:43 result =[]44 returnresult45
46 defcopy(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 returnresult52
53 defget_model(self, wheres):54 """通过条件获取一条记录"""
55 #如果有条件,则自动添加where
56 ifwheres:57 wheres = 'where' +wheres58
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 ifresult:65 returnresult[0]66 return{}67
68 def get_model_for_pk(self, pk, wheres=''):69 """通过主键值获取数据库记录实体"""
70 if notpk:71 return{}72 #组装查询条件
73 wheres = '%s = %s' % (self.__pk_name, str(pk))74
75 returnself.get_model(wheres)76
77 def get_value(self, column_name, wheres=''):78 """
79 获取指定条件的字段值————多于条记录时,只取第一条记录80 :param column_name: 单个字段名,如:id81 :param wheres: 查询条件82 :return: 7 (指定的字段值)83 """
84 if notcolumn_name:85 returnNone86 elifwheres:87 wheres = 'where' +wheres88
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 ifresult:94 returnresult[0].get(column_name)95
96 def get_value_list(self, column_name, wheres=''):97 """
98 获取指定条件记录的字段值列表99 :param column_name: 单个字段名,如:id100 :param wheres: 查询条件101 :return: [1,3,4,6,7]102 """
103 if notcolumn_name:104 column_name = self.__pk_name
105 elifwheres:106 wheres = 'where' +wheres107
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 andisinstance(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 infields.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 ifreturning:142 parameter['returning'] = ',' +returning143 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" %parameter148 #将生成好的字符串替字典参数值,生成最终可执行的sql语句
149 sql = sql %fields150
151 result =self.execute(sql)152 ifresult:153 returnresult[0]154 return{}155
156 def edit(self, fields, wheres='', returning=''):157 """批量编辑数据库记录"""
158 ### 拼接sql语句 ###
159 #拼接字段与值
160 field_list = [key + '= %(' + key + ')s' for key infields.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 ifwheres:169 parameter['wheres'] = 'where' +wheres170 else:171 parameter['wheres'] = ''
172
173 #如果有指定返回参数,则添加
174 ifreturning:175 parameter['returning'] = ',' +returning176 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" %parameter181 sql = sql %fields182
183 returnself.execute(sql)184
185 def edit_model(self, pk, fields, wheres='', returning=''):186 """编辑单条数据库记录"""
187 if notpk:188 return{}189 elifwheres:190 wheres = self.__pk_name + '=' + str(pk) + 'and' +wheres191 else:192 wheres = self.__pk_name + '=' +str(pk)193
194 returnself.edit(fields, wheres, returning)195
196 def delete(self, wheres='', returning='', is_update_cache=True):197 """批量删除数据库记录"""
198 #如果存在条件
199 ifwheres:200 wheres = 'where' +wheres201
202 #如果有指定返回参数,则添加
203 ifreturning:204 returning = ',' +returning205
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 returnself.execute(sql)210
211 def delete_model(self, pk, wheres='', returning='', is_update_cache=True):212 """删除单条数据库记录"""
213 if notpk:214 return{}215 elifwheres:216 wheres = self.__pk_name + '=' + str(pk) + 'and' +wheres217 else:218 wheres = self.__pk_name + '=' +str(pk)219
220 returnself.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 nottable_name:243 table_name = self.__table_name
244 #初始化查询字段名
245 if notcolumn_name_list:246 column_name_list = self.__column_name_list
247 #初始化查询条件
248 ifwheres:249 #如果是字符串,表示该查询条件已组装好了,直接可以使用
250 ifisinstance(wheres, str):251 wheres = 'where' +wheres252 #如果是list,则表示查询条件有多个,可以使用join将它们用and方式组合起来使用
253 elifisinstance(wheres, list):254 wheres = 'where' + 'and'.join(wheres)255 #初始化排序
256 if notorderby: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 isNone: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 returndata272
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_size283 else:284 page_total = data['records'] // page_size + 1
285 #判断页码是否超出限制,超出限制查询时会出现异常,所以将页面索引设置为最后一页
286 if page_number < 1 or page_number >page_total:287 page_number =page_total288 #记录总页面数量
289 data['total'] =page_total290 #记录当前页面值
291 data['page'] =page_number292 #计算当前页面要显示的记录起始位置(limit指定的位置)
293 record_number = (page_number - 1) *page_size294 #设置查询分页条件
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 ifresult:307 data['rows'] =result308 #不需要分页查询时,直接在这里设置总记录数
309 if page_size isNone:310 data['records'] =len(result)311
312 returndata313
314 def get_count(self, wheres=''):315 """获取指定条件记录数量"""
316 ifwheres:317 wheres = 'where' +wheres318 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 ifresult:323 return result[0].get('total')324 return0325
326 defget_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 return0335
336 defget_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 defget_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 #####################################################################
View Code
大家只要掌握了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 importredis5
6 from common importlog_helper7 from config importredis_config8
9 #设置redis配置参数
10 _redis =redis_config.REDIS11 #初始化Redis缓存链接
12 r =None13 try:14 if notr: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 exceptException 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 exceptException as e:39 log_helper.info('写缓存失败:key(' + key + ')' +str(e.args))40 pass
41
42
43 defget(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 exceptException as e:54 #log_helper.error('读缓存失败:key(' + key + ')' + str(e.args) + ' r:' + str(r) + ' _redis:' + str(_redis))
55 value =None56
57 return_str_to_json(value)58
59
60 defpush(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 exceptException as e:71 log_helper.info('写缓存失败:key(' + key + ')' +str(e.args))72 pass
73
74
75 defpop(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 exceptException as e:86 log_helper.info('读取缓存队列失败:key(' + key + ')' +str(e.args))87 value =None88
89 return_str_to_json(value)90
91
92 def_str_to_json(value):93 """
94 将缓存中读取出来的字符串转换成对应的数据、元组、列表或字典95 """
96 if notvalue:97 returnvalue98 #否则直接转换
99 try:100 value =value.decode()101 returneval(value)102 exceptException as e:103 print(e.args)104 pass
105 #否则直接输出字符串
106 returnvalue107
108
109 defdelete(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 exceptException as e:120 log_helper.info('Exception:' +str(e.args))121 pass
122
123
124 defclear():125 """
126 清空所有缓存127 """
128 try:129 r.flushdb()130 except:131 pass
View Code
我常用的是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写成一个方法来生成
defget_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)
接着是获取缓存对象方法
defget_model_for_cache(self, pk):"""从缓存中读取数据库记录"""
#生成缓存key
key =self.get_cache_key(pk)#从缓存中读取数据库记录
result =cache_helper.get(key)#缓存中不存在记录,则从数据库获取
if notresult:
result=self.get_model_for_pk(pk)
self.set_model_for_cache(pk, result)ifresult:returnresultelse:return {}
我们首先要做的同样是生成缓存key,然后调用get方法从缓存中读取对象,执行完后,需要判断该对象是否存在缓存中,如果不存在则表示该对象并未存储到缓存中或它可能存储过期了,所以需要重新从数据库中读取出来,并将它存储到缓存中,然后将读取出来的记录实体返回出去。
然后我们再增加一个读取指定记录字段值的方法
defget_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为空就可以解决这个问题
defdel_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 infields.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 ifwheres:21 parameter['wheres'] = 'where' +wheres22 else:23 parameter['wheres'] = ''
24
25 #如果有指定返回参数,则添加
26 ifreturning:27 parameter['returning'] = ',' +returning28 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" %parameter33 sql = sql %fields34
35 result =self.execute(sql)36 ifresult:37 #判断是否删除对应的缓存
38 ifis_update_cache:39 #循环删除更新成功的所有记录对应的缓存
40 for model inresult: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 ifwheres:11 wheres = 'where' +wheres12
13 #如果有指定返回参数,则添加
14 ifreturning:15 returning = ',' +returning16
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 ifresult:22 #同步删除对应的缓存
23 ifis_update_cache:24 for model inresult: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 =db5 #是否输出执行的Sql语句到日志中
6 self.__is_output_sql =is_output_sql7 #表名称
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 defadd_relevance_cache_in_list(self, key):2 """将缓存名称存储到列表里————主要存储与记录变更关联的"""
3 #从nosql中读取全局缓存列表
4 cache_list = cache_helper.get(self.__cache_list)5 #判断缓存列表是否有值,有则进行添加操作
6 ifcache_list:7 #判断是否已存储列表中,不存在则执行添加操作
8 if not key incache_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 defdel_relevance_cache(self):2 """删除关联缓存————将和数据表记录关联的,个性化缓存全部删除"""
3 #从nosql中读取全局缓存列表
4 cache_list = cache_helper.get(self.__cache_list)5 #清除已删除缓存列表
6 cache_helper.delete(self.__cache_list)7 ifcache_list:8 #执行删除操作
9 for cache incache_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 infields.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 ifwheres:21 parameter['wheres'] = 'where' +wheres22 else:23 parameter['wheres'] = ''
24
25 #如果有指定返回参数,则添加
26 ifreturning:27 parameter['returning'] = ',' +returning28 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" %parameter33 sql = sql %fields34
35 result =self.execute(sql)36 ifresult:37 #判断是否删除对应的缓存
38 ifis_update_cache:39 #循环删除更新成功的所有记录对应的缓存
40 for model inresult:41 self.del_model_for_cache(model.get('id', 0))42 #同步删除与本表关联的缓存
43 self.del_relevance_cache()44 returnresult45
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 ifwheres:56 wheres = 'where' +wheres57
58 #如果有指定返回参数,则添加
59 ifreturning:60 returning = ',' +returning61
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 ifresult:67 #同步删除对应的缓存
68 ifis_update_cache:69 for model inresult:70 self.del_model_for_cache(model.get('id', 0))71 #同步删除与本表关联的缓存
72 self.del_relevance_cache()73 return result
View Code
ORM的缓存改造就全部完成了,下面是完整代码
1 #!/usr/bin/env python
2 #coding=utf-8
3
4 from common importdb_helper, cache_helper5
6
7 classLogicBase():8 """逻辑层基础类"""
9
10 def __init__(self, db, is_output_sql, table_name, column_name_list='*', pk_name='id'):11 """类初始化"""
12 #数据库参数
13 self.__db =db14 #是否输出执行的Sql语句到日志中
15 self.__is_output_sql =is_output_sql16 #表名称
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 defselect(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 notresult:34 result =[]35 returnresult36
37 defexecute(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 ifresult:43 db.commit()44 else:45 result =[]46 returnresult47
48 defcopy(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 returnresult54
55 defget_model(self, wheres):56 """通过条件获取一条记录"""
57 #如果有条件,则自动添加where
58 ifwheres:59 wheres = 'where' +wheres60
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 ifresult:67 returnresult[0]68 return{}69
70 def get_model_for_pk(self, pk, wheres=''):71 """通过主键值获取数据库记录实体"""
72 if notpk:73 return{}74 #组装查询条件
75 wheres = '%s = %s' % (self.__pk_name, str(pk))76
77 returnself.get_model(wheres)78
79 def get_value(self, column_name, wheres=''):80 """
81 获取指定条件的字段值————多于条记录时,只取第一条记录82 :param column_name: 单个字段名,如:id83 :param wheres: 查询条件84 :return: 7 (指定的字段值)85 """
86 if notcolumn_name:87 returnNone88 elifwheres:89 wheres = 'where' +wheres90
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 ifresult:96 returnresult[0].get(column_name)97
98 def get_value_list(self, column_name, wheres=''):99 """
100 获取指定条件记录的字段值列表101 :param column_name: 单个字段名,如:id102 :param wheres: 查询条件103 :return: [1,3,4,6,7]104 """
105 if notcolumn_name:106 column_name = self.__pk_name
107 elifwheres:108 wheres = 'where' +wheres109
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 andisinstance(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 infields.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 ifreturning:144 parameter['returning'] = ',' +returning145 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" %parameter150 #将生成好的字符串替字典参数值,生成最终可执行的sql语句
151 sql = sql %fields152
153 result =self.execute(sql)154 ifresult:155 returnresult[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 infields.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 ifwheres:178 parameter['wheres'] = 'where' +wheres179 else:180 parameter['wheres'] = ''
181
182 #如果有指定返回参数,则添加
183 ifreturning:184 parameter['returning'] = ',' +returning185 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" %parameter190 sql = sql %fields191
192 result =self.execute(sql)193 ifresult:194 #判断是否删除对应的缓存
195 ifis_update_cache:196 #循环删除更新成功的所有记录对应的缓存
197 for model inresult:198 self.del_model_for_cache(model.get('id', 0))199 #同步删除与本表关联的缓存
200 self.del_relevance_cache()201 returnresult202
203 def edit_model(self, pk, fields, wheres='', returning=''):204 """编辑单条数据库记录"""
205 if notpk:206 return{}207 elifwheres:208 wheres = self.__pk_name + '=' + str(pk) + 'and' +wheres209 else:210 wheres = self.__pk_name + '=' +str(pk)211
212 returnself.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 ifwheres:224 wheres = 'where' +wheres225
226 #如果有指定返回参数,则添加
227 ifreturning:228 returning = ',' +returning229
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 ifresult:235 #同步删除对应的缓存
236 ifis_update_cache:237 for model inresult:238 self.del_model_for_cache(model.get('id', 0))239 #同步删除与本表关联的缓存
240 self.del_relevance_cache()241 returnresult242
243 def delete_model(self, pk, wheres='', returning='', is_update_cache=True):244 """删除单条数据库记录"""
245 if notpk:246 return{}247 elifwheres:248 wheres = self.__pk_name + '=' + str(pk) + 'and' +wheres249 else:250 wheres = self.__pk_name + '=' +str(pk)251
252 returnself.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 nottable_name:275 table_name = self.__table_name
276 #初始化查询字段名
277 if notcolumn_name_list:278 column_name_list = self.__column_name_list
279 #初始化查询条件
280 ifwheres:281 #如果是字符串,表示该查询条件已组装好了,直接可以使用
282 ifisinstance(wheres, str):283 wheres = 'where' +wheres284 #如果是list,则表示查询条件有多个,可以使用join将它们用and方式组合起来使用
285 elifisinstance(wheres, list):286 wheres = 'where' + 'and'.join(wheres)287 #初始化排序
288 if notorderby: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 isNone: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 returndata304
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_size315 else:316 page_total = data['records'] // page_size + 1
317 #判断页码是否超出限制,超出限制查询时会出现异常,所以将页面索引设置为最后一页
318 if page_number < 1 or page_number >page_total:319 page_number =page_total320 #记录总页面数量
321 data['total'] =page_total322 #记录当前页面值
323 data['page'] =page_number324 #计算当前页面要显示的记录起始位置(limit指定的位置)
325 record_number = (page_number - 1) *page_size326 #设置查询分页条件
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 ifresult:339 data['rows'] =result340 #不需要分页查询时,直接在这里设置总记录数
341 if page_size isNone:342 data['records'] =len(result)343
344 returndata345
346 def get_count(self, wheres=''):347 """获取指定条件记录数量"""
348 ifwheres:349 wheres = 'where' +wheres350 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 ifresult:355 return result[0].get('total')356 return0357
358 defget_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 return0367
368 defget_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 defget_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 defget_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 defget_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 notresult:411 result =self.get_model_for_pk(pk)412 self.set_model_for_cache(pk, result)413 ifresult:414 returnresult415 else:416 return{}417
418 defget_value_for_cache(self, pk, column_name):419 """获取指定记录的字段值"""
420 returnself.get_model_for_cache(pk).get(column_name)421
422 defdel_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 defadd_relevance_cache_in_list(self, key):431 """将缓存名称存储到列表里————主要存储与记录变更关联的"""
432 #从nosql中读取全局缓存列表
433 cache_list = cache_helper.get(self.__cache_list)434 #判断缓存列表是否有值,有则进行添加操作
435 ifcache_list:436 #判断是否已存储列表中,不存在则执行添加操作
437 if not key incache_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 defdel_relevance_cache(self):446 """删除关联缓存————将和数据表记录关联的,个性化缓存全部删除"""
447 #从nosql中读取全局缓存列表
448 cache_list = cache_helper.get(self.__cache_list)449 #清除已删除缓存列表
450 cache_helper.delete(self.__cache_list)451 ifcache_list:452 #执行删除操作
453 for cache incache_list:454 cache_helper.delete(cache)455
456 #####################################################################
View Code
版权声明:本文原创发表于 博客园,作者为
python开发QQ群:669058475(本群已满)、733466321(可以加2群) 作者博客:http://www.cnblogs.com/EmptyFS/