我的第一个python web开发框架(30)——定制ORM(六)
在开发中,查询操作是使用最多的,而查询列表是其中之一,查询列表可分为分页查询和不分页查询(它们之间多了一次总记录数查询),还可以分为单表查询和多表关联查询,返回的结构体根据前端使用的表单框架不同而有所区别。
我们先看看,对于列表分页查询,在接口中是如何处理的
@get('/api/product/')
def callback():
"""
获取列表数据
"""
# 设置查询条件
wheres = ''
# 产品分类id
product_class_id = convert_helper.to_int0(web_helper.get_query('product_class_id', '产品分类id', is_check_null=False))
if product_class_id > 0:
wheres = 'where product_class_id=' + str(product_class_id)
# 页面索引
page_number = convert_helper.to_int1(web_helper.get_query('page', '', is_check_null=False))
# 页面显示记录数量
page_size = convert_helper.to_int0(web_helper.get_query('rows', '', is_check_null=False))
# 排序字段
sidx = web_helper.get_query('sidx', '', is_check_null=False)
# 顺序还是倒序排序
sord = web_helper.get_query('sord', '', is_check_null=False)
# 初始化排序字段
order_by = 'id desc'
### 设置排序 ###
if sidx:
order_by = sidx + ' ' + sord
# 类型
type = web_helper.get_query('type', '类型', is_check_null=False)
# 判断是否是前台提交获取数据
if type != 'backstage':
# 判断是否已经存在查询条件了,是的话在原查询条件后面拼接
if wheres:
wheres = wheres + ' and is_enable=1'
else:
wheres = 'where is_enable=1' #############################################################
# 初始化输出格式(前端使用jqgrid列表,需要指定输出格式)
data = {
'records': 0,
'total': 0,
'page': 1,
'rows': [],
}
#############################################################
# 执行sql,获取指定条件的记录总数量
sql = 'select count(1) as records from product %(wheres)s' % {'wheres': wheres}
result = db_helper.read(sql)
# 如果查询失败或不存在指定条件记录,则直接返回初始值
if not result or result[0]['records'] == 0:
return data
# 保存总记录数量
data['records'] = result[0].get('records', 0) #############################################################
### 设置分页索引与页面大小 ###
# 设置分页大小
if page_size is None or page_size <= 0:
page_size = 10
# 计算总页数
if data['records'] % page_size == 0:
page_total = data['records'] // page_size
else:
page_total = data['records'] // page_size + 1
# 记录总页面数量
data['total'] = page_total # 判断提交的页码是否超出范围
if page_number < 1 or page_number > page_total:
page_number = page_total
# 记录当前页面索引值
data['page'] = page_number # 计算当前页面要显示的记录起始位置
record_number = (page_number - 1) * page_size
# 设置查询分页条件
paging = ' limit ' + str(page_size) + ' offset ' + str(record_number) ############################################################# # 组合sql查询语句
sql = "select * from product %(wheres)s order by %(orderby)s %(paging)s" % \
{'wheres': wheres, 'orderby': order_by, 'paging': paging}
# 读取记录
result = db_helper.read(sql)
if result:
# 存储记录
data['rows'] = result if data:
# 直接输出json
return web_helper.return_raise(json.dumps(data, cls=json_helper.CJsonEncoder))
else:
return web_helper.return_msg(-1, "查询失败")
代码看起来很长,有点复杂,对于这种列表分页查询,如果不封装的话,开发时复制粘贴就很容易出错,所以我们需要重新处理才行。
从上面代码可以看到,具体功能分为几个部分:
第一部分(9到33行)是接收并组合查询条件,接收分页参数和排序参数
第二部分(37到42行)是初始化结果输出参数
第三部分(44到51行)是获取查询总记录数
第四部分(55到75行)是计算总页数,计算当前分页位置要显示的记录位置区间
第五部分(80到92行)是组合查询语句,查询并输出结果
除了产品列表这个接口,大家可以看看产品分类列表接口,会发现两个接口第二部分到第五部分都差不多,所以我们封装ORM时,可以将这些相似部分进行处理,将它们封装到ORM对应的方法里。
首先,我们对上面代码的分析,可以提炼出分页查询方法需要有下面参数:查询字段、查询条件、当前分页索引值、每页显示记录数量、排序。如果是多表查询时,我们的ORM是直接绑定当前表单的就不适用了,所以还需要有个设置表名的参数,好灵活处理各种需求,根据这些要求,我们可以创建好列表查询方法:
def get_list(self, column_name_list='', wheres='', page_number=None, page_size=None, orderby=None, table_name=None):
"""
获取指定条件的数据库记录集
:param column_name_list: 查询字段
:param wheres: 查询条件
:param page_number: 分页索引值
:param page_size: 分页大小, 存在值时才会执行分页
:param orderby: 排序规则
:param table_name: 查询数据表,多表查询时需要设置
:return: 返回记录集总数量与分页记录集
{'records': 0, 'total': 0, 'page': 0, 'rows': []}
"""
在接收到这些参数以后,我们需要对相关参数进行初始化操作,方便后续代码的执行
# 初始化输出参数:总记录数量与列表集
data = {
'records': 0, # 总记录数
'total': 0, # 总页数
'page': 1, # 当前页面索引
'rows': [], # 查询结果(记录列表)
}
# 初始化查询数据表名称
if not table_name:
table_name = self.__table_name
# 初始化查询字段名
if not column_name_list:
column_name_list = self.__column_name_list
# 初始化查询条件
if wheres:
# 如果是字符串,表示该查询条件已组装好了,直接可以使用
if isinstance(wheres, str):
wheres = 'where ' + wheres
# 如果是list,则表示查询条件有多个,可以使用join将它们用and方式组合起来使用
elif isinstance(wheres, list):
wheres = 'where ' + ' and '.join(wheres)
# 初始化排序
if not orderby:
orderby = self.__pk_name + ' desc'
# 初始化分页查询的记录区间
paging = ''
这里是对传入的参数和后续需要用到的参数进行初始化操作
这里需要初始化查询结果输出参数结构,在进行记录数查询时,如果没有记录存在,就可以直接将结果返回出去了;
默认数据表为当前类实体指定的表名称,如果进行多表联合查询时,就需要设置多表联合查询的组合表名称,比如:product left join product_class on product.product_class_id = product_class.id
同时我们还需要设置查询字段内容,如果想查询出所有字段,直接使用*,如果只想要输出指定的几个字段值,则可以填写这几个字段值,比如:id,name,content
在查询时,有时不需要查询条件,这时我们可以不填写条件,如果有指定条件时,我们可以将它们组合好,也可以放到list中。它们的区别在于,有多个查询条件时,我们有时很难判断当前条件前需不需要添加and,这时我们就可以使用' and '.join(列表) 来进行合成了,当然用list方式条件之间只能是and的关系。对于复杂的条件,我们可以组合好以后提交进来直接使用;
在查询时,如果没有指定排序方式,我们默认使用主键倒序来进行排序
在分页列表操作时,我们通常需要获取总记录数返回给前端,所以在执行查询前,我们需要获取当前查询条件的总记录数
with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
#############################################################
# 判断是否需要进行分页
if not page_size is None:
### 执行sql,获取指定条件的记录总数量
sql = 'select count(1) as records from %(table_name)s %(wheres)s ' % \
{'table_name': table_name, 'wheres': wheres}
result = db.execute(sql)
# 如果查询失败或不存在指定条件记录,则直接返回初始值
if not result or result[0]['records'] == 0:
return data # 设置记录总数量
data['records'] = result[0].get('records')
加上if not page_size is None判断,是因为有时候我们查询时,不需要分页操作,直接将所有记录输出了,这里加上判断可以减少不必要的记录总数量查询
当我们获取到总记录数量以后,我们需要根据前端页面显示的记录数进行计算,计算出总页面数量,排除页面索引值超出限制可能会带来的异常,还有需要计算当前页面查询时对应的记录起始位置,组合分页查询条件pagin
#########################################################
### 设置分页索引与页面大小 ###
if page_size <= 0:
page_size = 10
# 计算总分页数量:通过总记录数除于每页显示数量来计算总分页数量
if data['records'] % page_size == 0:
page_total = data['records'] // page_size
else:
page_total = data['records'] // page_size + 1
# 判断页码是否超出限制,超出限制查询时会出现异常,所以将页面索引设置为最后一页
if page_number < 1 or page_number > page_total:
page_number = page_total
# 记录总页面数量
data['total'] = page_total
# 记录当前页面值
data['page'] = page_number
# 计算当前页面要显示的记录起始位置(limit指定的位置)
record_number = (page_number - 1) * page_size
# 设置查询分页条件
paging = ' limit ' + str(page_size) + ' offset ' + str(record_number)
#############################################################
最后,我们组合最终查询条件,查询并输出结果
### 按条件查询数据库记录
sql = "select %(column_name_list)s from %(table_name)s %(wheres)s order by %(orderby)s %(paging)s" % \
{'column_name_list': column_name_list,
'table_name': table_name,
'wheres': wheres,
'orderby': orderby,
'paging': paging}
result = db.execute(sql)
if result:
data['rows'] = result
# 不需要分页查询时,直接在这里设置总记录数
if page_size is None:
data['records'] = len(result) return data
完整代码
def get_list(self, column_name_list='', wheres='', page_number=None, page_size=None, orderby=None, table_name=None):
"""
获取指定条件的数据库记录集
:param column_name_list: 查询字段
:param wheres: 查询条件
:param page_number: 分页索引值
:param page_size: 分页大小, 存在值时才会执行分页
:param orderby: 排序规则
:param table_name: 查询数据表,多表查询时需要设置
:return: 返回记录集总数量与分页记录集
{'records': 0, 'total': 0, 'page': 0, 'rows': []}
"""
# 初始化输出参数:总记录数量与列表集
data = {
'records': 0, # 总记录数
'total': 0, # 总页数
'page': 1, # 当前页面索引
'rows': [], # 查询结果(记录列表)
}
# 初始化查询数据表名称
if not table_name:
table_name = self.__table_name
# 初始化查询字段名
if not column_name_list:
column_name_list = self.__column_name_list
# 初始化查询条件
if wheres:
# 如果是字符串,表示该查询条件已组装好了,直接可以使用
if isinstance(wheres, str):
wheres = 'where ' + wheres
# 如果是list,则表示查询条件有多个,可以使用join将它们用and方式组合起来使用
elif isinstance(wheres, list):
wheres = 'where ' + ' and '.join(wheres)
# 初始化排序
if not orderby:
orderby = self.__pk_name + ' desc'
# 初始化分页查询的记录区间
paging = '' with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
#############################################################
# 判断是否需要进行分页
if not page_size is None:
### 执行sql,获取指定条件的记录总数量
sql = 'select count(1) as records from %(table_name)s %(wheres)s ' % \
{'table_name': table_name, 'wheres': wheres}
result = db.execute(sql)
# 如果查询失败或不存在指定条件记录,则直接返回初始值
if not result or result[0]['records'] == 0:
return data # 设置记录总数量
data['records'] = result[0].get('records') #########################################################
### 设置分页索引与页面大小 ###
if page_size <= 0:
page_size = 10
# 计算总分页数量:通过总记录数除于每页显示数量来计算总分页数量
if data['records'] % page_size == 0:
page_total = data['records'] // page_size
else:
page_total = data['records'] // page_size + 1
# 判断页码是否超出限制,超出限制查询时会出现异常,所以将页面索引设置为最后一页
if page_number < 1 or page_number > page_total:
page_number = page_total
# 记录总页面数量
data['total'] = page_total
# 记录当前页面值
data['page'] = page_number
# 计算当前页面要显示的记录起始位置(limit指定的位置)
record_number = (page_number - 1) * page_size
# 设置查询分页条件
paging = ' limit ' + str(page_size) + ' offset ' + str(record_number)
############################################################# ### 按条件查询数据库记录
sql = "select %(column_name_list)s from %(table_name)s %(wheres)s order by %(orderby)s %(paging)s" % \
{'column_name_list': column_name_list,
'table_name': table_name,
'wheres': wheres,
'orderby': orderby,
'paging': paging}
result = db.execute(sql)
if result:
data['rows'] = result
# 不需要分页查询时,直接在这里设置总记录数
if page_size is None:
data['records'] = len(result) return data
我们在单元测试中跑一跑,看看结果吧
#!/usr/bin/evn python
# coding=utf-8 import unittest
from common.string_helper import string
from logic import product_logic class DbHelperTest(unittest.TestCase):
"""数据库操作包测试类""" def setUp(self):
"""初始化测试环境"""
print('------ini------') def tearDown(self):
"""清理测试环境"""
print('------clear------') def test(self):
##############################################
# 只需要看这里,其他代码是测试用例的模板代码 #
##############################################
# 实例化product表操作类ProductLogic
_product_logic = product_logic.ProductLogic()
result = _product_logic.get_list('*', '', 1, 2)
print(result) ############################################## if __name__ == '__main__':
unittest.main()
输出结果
-- -- --ini-- -- --
{
'records': 4,
'total': 1,
'page': 1,
'rows': [{
'content': '',
'name': '名称',
'place_of_origin': '',
'front_cover_img': '',
'code': '',
'quality_guarantee_period': '',
'product_class_id': 1,
'standard': '',
'add_time': datetime.datetime(2018, 8, 3, 16, 51, 3),
'id': 15,
'is_enable': 0
}, {
'content': '',
'name': '张三',
'place_of_origin': '',
'front_cover_img': '',
'code': '',
'quality_guarantee_period': '',
'product_class_id': 0,
'standard': '',
'add_time': datetime.datetime(2018, 8, 3, 0, 14, 14),
'id': 14,
'is_enable': 0
}]
}
-- -- --clear-- -- --
前面的接口我们也改造一下
@get('/api/product/')
def callback():
"""
获取列表数据
"""
# 产品分类id
product_class_id = convert_helper.to_int0(web_helper.get_query('product_class_id', '产品分类id', is_check_null=False))
# 类型
type = web_helper.get_query('type', '类型', is_check_null=False)
# 页面索引
page_number = convert_helper.to_int1(web_helper.get_query('page', '', is_check_null=False))
# 页面显示记录数量
page_size = convert_helper.to_int0(web_helper.get_query('rows', '', is_check_null=False))
# 排序字段
sidx = web_helper.get_query('sidx', '', is_check_null=False)
# 顺序还是倒序排序
sord = web_helper.get_query('sord', '', is_check_null=False) # 设置查询条件
wheres = []
if product_class_id > 0:
wheres.append('product_class_id=' + str(product_class_id))
# 判断是否是前台提交获取数据
if type != 'backstage':
wheres.append('is_enable=1') # 初始化排序字段
orderby = None
### 设置排序 ###
if sidx:
orderby = sidx + ' ' + sord # 实例化product表操作类ProductLogic
_product_logic = product_logic.ProductLogic()
result = _product_logic.get_list('*', wheres, page_number, page_size, orderby)
if result:
return web_helper.return_raise(json.dumps(result, cls=json_helper.CJsonEncoder))
else:
return web_helper.return_msg(-1, "查询失败")
这样处理以后,代码看起来舒服多了
版权声明:本文原创发表于 博客园,作者为 AllEmpty 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
python开发QQ群:669058475(本群已满)、733466321(可以加2群) 作者博客:http://www.cnblogs.com/EmptyFS/
我的第一个python web开发框架(30)——定制ORM(六)的更多相关文章
- 我的第一个python web开发框架(14)——后台管理系统登录功能
接下来正式进入网站的功能开发.要完成后台管理系统登录功能,通过查看登录页面,我们可以了解到,我们需要编写验证码图片获取接口和登录处理接口,然后在登录页面的HTML上编写AJAX. 在进行接口开发之前, ...
- 我的第一个python web开发框架(41)——总结
我的第一个python web开发框架系列博文从17年6.7月份开始写(存了近十章稿留到9月份才开始发布),到今天结束,一年多时间,想想真不容易啊. 整个过程断断续续,中间有段时间由于工作繁忙停了好长 ...
- 我的第一个python web开发框架(1)——前言
由于之前经验不是很丰富,写的C#系统太过复杂,所以一直想重写,但学的越多越觉得自己懂的越少,越觉的底气不足.所以一直不敢动手,在内心深处对自己讲,要静下心来认真学习,继续沉淀沉淀.这两年多以来找各种机 ...
- 我的第一个python web开发框架(3)——怎么开始?
小白与小美公司经过几次接触商谈,好不容易将外包签订了下来,准备开始大干一场.不过小白由于没有太多的项目经验,学过python懂得python的基本语法,在公司跟着大家做过简单功能,另外还会一些HTML ...
- 我的第一个python web开发框架(22)——一个安全小事故
在周末的一个早上,小白还在做着美梦,就收到了小美的连环追魂call,电话一直响个不停. 小白打着哈欠拿起电话:早上好美女. 小美:出事了出事了,我们公司网站一早访问是一片空白,什么内容都没有了,你赶急 ...
- 我的第一个python web开发框架(21)——小结
这个小网站终于成功上线,小白除了收获一笔不多的费用外,还得到女神小美的赞赏,心中满满的成就感.这一天下班后,他请老菜一起下馆子,兑现请吃饭的承诺,顺便让老菜点评一下. 小白:老大,在你的指导下终于完成 ...
- 我的第一个python web开发框架(2)——一个简单的小外包
第一部分说明 第一部分大概有20来章,主要讲的是一些开发常识.开发前中后期准备内容.开发环境与服务器部署环境安装设置.python基础框架结构与功能等内容,代码会比较简单. 本系列会以故事的方式,向大 ...
- 我的第一个python web开发框架(6)——第一个Hello World
小白中午听完老菜讲的那些话后一直在思考,可想来想去还是一头雾水,晕晕呼呼的一知半解,到最后还是想不明白,心想:老大讲的太高深了,只能听懂一半半,看来只能先记下来,将明白的先做,不明白的等以后遇到再学. ...
- 我的第一个python web开发框架(7)——本地部署前端访问服务器
PS:本系列内容进度节奏会放的很慢,每次知识点都尽量少一点,这样大家接触的知识点少了,会更容易理解,因为少即是多.另外,对于后面代码部分,虽然尽量不用那些复杂的封装和类,但它并不表示看了就能全部记住, ...
随机推荐
- 6.Django session
session 1.概述 cookie和session的区别 Cookie是保存在用户浏览器端的键值对,Session是保存在服务器端的键值对:Cookie做用户验证的时,敏感信息不适合放在Cooki ...
- 排查线上问题常用的几个Linux命令
top 相当于Windows任务管理器 可以看到,输出结果分两部分,前5行是总览,下面是具体的进程资源占用情况.下面逐行看一下 第1行 top - 18:14:58 up 112 days, 1:3 ...
- asp.net core系列 23 EF模型配置(概述, 类型和属性的包含与排除)
一.模型配置概述 EF使用一组约定基于实体类的定义来构建模型. 可指定其他配置以补充或替代约定的内容.本系列介绍的配置可应用于面向任何数据存储的模型,以及面向任意关系数据库时可应用的配置. 数据库提供 ...
- C#2.0 迭代器
迭代器 迭代器模式是和为模式的一种范例,我们访问数据序列中所有的元素,不用关心序列是什么类型.从数据管道中数据经过一系列不同的转换或过滤后从管道的另一端出来. 像数组.集合等已经内置了迭代器,我们可以 ...
- Android总结篇系列:Activity启动模式(lauchMode)
本来想针对Activity中的启动模式写篇文章的,后来网上发现有人已经总结的相当好了,在此直接引用过来,并加上自己的一些理解,在此感谢原作者. 文章地址: http://blog.csdn.net/l ...
- 如何将视频导入到ipad中并播放
首先在电脑上下载并安装itunes,然后用apple账号登入, 在ipad上从apple store中下载一个播放器如KMPlayer 点击itunes上小手机的图标,找到文件共享,选中应用KMPla ...
- Java并发专题(一)认识线程
1.1 认识线程 线程是轻量级进程,也是程序执行的一个路径,每一个线程都有自己的局部变量表.程序计数器(指向正在执行的指令指针)以及各自的生命周期,现代操作系统中一般不止一个线程在运行.比如说,当我们 ...
- 浅谈RNN、LSTM + Kreas实现及应用
本文主要针对RNN与LSTM的结构及其原理进行详细的介绍,了解什么是RNN,RNN的1对N.N对1的结构,什么是LSTM,以及LSTM中的三门(input.ouput.forget),后续将利用深度学 ...
- Perl多线程(2):数据共享和线程安全
线程数据共享 在介绍Perl解释器线程的时候一直强调,Perl解释器线程在被创建出来的时候,将从父线程中拷贝数据到子线程中,使得数据是线程私有的,并且数据是线程隔离的.如果真的想要在线程间共享数据,需 ...
- arcgis 10.0中的server报错说工作站服务没有打开
大家好! 写这篇文章其实我也不知道该不该写,感觉问题其实也不是自己解决的,但是这个问题困恼了我2天,我还将arcgis10.0重装了一次. 下面也不多说了,主要是由于公司的需求,将自己的arcgis1 ...