我的第一个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             #####################################################################
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 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
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写成一个方法来生成

    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
View Code

  

  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     #####################################################################
View Code

 

 

版权声明:本文原创发表于 博客园,作者为 AllEmpty 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

python开发QQ群:669058475(本群已满)、733466321(可以加2群)    作者博客:http://www.cnblogs.com/EmptyFS/

posted @ 2018-08-16 23:52  AllEmpty  阅读(1517)  评论(0编辑  收藏  举报