• 我的第一个python web开发框架(30)——定制ORM(六)


      在开发中,查询操作是使用最多的,而查询列表是其中之一,查询列表可分为分页查询和不分页查询(它们之间多了一次总记录数查询),还可以分为单表查询和多表关联查询,返回的结构体根据前端使用的表单框架不同而有所区别。

      我们先看看,对于列表分页查询,在接口中是如何处理的

     1 @get('/api/product/')
     2 def callback():
     3     """
     4     获取列表数据
     5     """
     6     # 设置查询条件
     7     wheres = ''
     8     # 产品分类id
     9     product_class_id = convert_helper.to_int0(web_helper.get_query('product_class_id', '产品分类id', is_check_null=False))
    10     if product_class_id > 0:
    11         wheres = 'where product_class_id=' + str(product_class_id)
    12     # 页面索引
    13     page_number = convert_helper.to_int1(web_helper.get_query('page', '', is_check_null=False))
    14     # 页面显示记录数量
    15     page_size = convert_helper.to_int0(web_helper.get_query('rows', '', is_check_null=False))
    16     # 排序字段
    17     sidx = web_helper.get_query('sidx', '', is_check_null=False)
    18     # 顺序还是倒序排序
    19     sord = web_helper.get_query('sord', '', is_check_null=False)
    20     # 初始化排序字段
    21     order_by = 'id desc'
    22     ### 设置排序 ###
    23     if sidx:
    24         order_by = sidx + ' ' + sord
    25     # 类型
    26     type = web_helper.get_query('type', '类型', is_check_null=False)
    27     # 判断是否是前台提交获取数据
    28     if type != 'backstage':
    29         # 判断是否已经存在查询条件了,是的话在原查询条件后面拼接
    30         if wheres:
    31             wheres = wheres + ' and is_enable=1'
    32         else:
    33             wheres = 'where is_enable=1'
    34 
    35     #############################################################
    36     # 初始化输出格式(前端使用jqgrid列表,需要指定输出格式)
    37     data = {
    38         'records': 0,
    39         'total': 0,
    40         'page': 1,
    41         'rows': [],
    42     }
    43     #############################################################
    44     # 执行sql,获取指定条件的记录总数量
    45     sql = 'select count(1) as records from product %(wheres)s' % {'wheres': wheres}
    46     result = db_helper.read(sql)
    47     # 如果查询失败或不存在指定条件记录,则直接返回初始值
    48     if not result or result[0]['records'] == 0:
    49         return data
    50     # 保存总记录数量
    51     data['records'] = result[0].get('records', 0)
    52 
    53     #############################################################
    54     ### 设置分页索引与页面大小 ###
    55     # 设置分页大小
    56     if page_size is None or page_size <= 0:
    57         page_size = 10
    58     # 计算总页数
    59     if data['records'] % page_size == 0:
    60         page_total = data['records'] // page_size
    61     else:
    62         page_total = data['records'] // page_size + 1
    63     # 记录总页面数量
    64     data['total'] = page_total
    65 
    66     # 判断提交的页码是否超出范围
    67     if page_number < 1 or page_number > page_total:
    68         page_number = page_total
    69     # 记录当前页面索引值
    70     data['page'] = page_number
    71 
    72     # 计算当前页面要显示的记录起始位置
    73     record_number = (page_number - 1) * page_size
    74     # 设置查询分页条件
    75     paging = ' limit ' + str(page_size) + ' offset ' + str(record_number)
    76 
    77     #############################################################
    78 
    79     # 组合sql查询语句
    80     sql = "select * from product %(wheres)s order by %(orderby)s %(paging)s" % 
    81            {'wheres': wheres, 'orderby': order_by, 'paging': paging}
    82     # 读取记录
    83     result = db_helper.read(sql)
    84     if result:
    85         # 存储记录
    86         data['rows'] = result
    87 
    88     if data:
    89         # 直接输出json
    90         return web_helper.return_raise(json.dumps(data, cls=json_helper.CJsonEncoder))
    91     else:
    92         return web_helper.return_msg(-1, "查询失败")
    View Code

      代码看起来很长,有点复杂,对于这种列表分页查询,如果不封装的话,开发时复制粘贴就很容易出错,所以我们需要重新处理才行。

      从上面代码可以看到,具体功能分为几个部分:

      第一部分(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': []}
        """

      在接收到这些参数以后,我们需要对相关参数进行初始化操作,方便后续代码的执行

     1     # 初始化输出参数:总记录数量与列表集
     2     data = {
     3         'records': 0,   # 总记录数
     4         'total': 0,     # 总页数
     5         'page': 1,      # 当前页面索引
     6         'rows': [],     # 查询结果(记录列表)
     7     }
     8     # 初始化查询数据表名称
     9     if not table_name:
    10         table_name = self.__table_name
    11     # 初始化查询字段名
    12     if not column_name_list:
    13         column_name_list = self.__column_name_list
    14     # 初始化查询条件
    15     if wheres:
    16         # 如果是字符串,表示该查询条件已组装好了,直接可以使用
    17         if isinstance(wheres, str):
    18             wheres = 'where ' + wheres
    19         # 如果是list,则表示查询条件有多个,可以使用join将它们用and方式组合起来使用
    20         elif isinstance(wheres, list):
    21             wheres = 'where ' + ' and '.join(wheres)
    22     # 初始化排序
    23     if not orderby:
    24         orderby = self.__pk_name + ' desc'
    25     # 初始化分页查询的记录区间
    26     paging = ''

      这里是对传入的参数和后续需要用到的参数进行初始化操作

      这里需要初始化查询结果输出参数结构,在进行记录数查询时,如果没有记录存在,就可以直接将结果返回出去了;

      默认数据表为当前类实体指定的表名称,如果进行多表联合查询时,就需要设置多表联合查询的组合表名称,比如:product left join product_class on product.product_class_id = product_class.id

      同时我们还需要设置查询字段内容,如果想查询出所有字段,直接使用*,如果只想要输出指定的几个字段值,则可以填写这几个字段值,比如:id,name,content

      在查询时,有时不需要查询条件,这时我们可以不填写条件,如果有指定条件时,我们可以将它们组合好,也可以放到list中。它们的区别在于,有多个查询条件时,我们有时很难判断当前条件前需不需要添加and,这时我们就可以使用' and '.join(列表) 来进行合成了,当然用list方式条件之间只能是and的关系。对于复杂的条件,我们可以组合好以后提交进来直接使用;

      在查询时,如果没有指定排序方式,我们默认使用主键倒序来进行排序

      在分页列表操作时,我们通常需要获取总记录数返回给前端,所以在执行查询前,我们需要获取当前查询条件的总记录数

     1     with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
     2         #############################################################
     3         # 判断是否需要进行分页
     4         if not page_size is None:
     5             ### 执行sql,获取指定条件的记录总数量
     6             sql = 'select count(1) as records from %(table_name)s %(wheres)s ' % 
     7                   {'table_name': table_name, 'wheres': wheres}
     8             result = db.execute(sql)
     9             # 如果查询失败或不存在指定条件记录,则直接返回初始值
    10             if not result or result[0]['records'] == 0:
    11                 return data
    12 
    13             # 设置记录总数量
    14             data['records'] = result[0].get('records')

      加上if not page_size is None判断,是因为有时候我们查询时,不需要分页操作,直接将所有记录输出了,这里加上判断可以减少不必要的记录总数量查询

      当我们获取到总记录数量以后,我们需要根据前端页面显示的记录数进行计算,计算出总页面数量,排除页面索引值超出限制可能会带来的异常,还有需要计算当前页面查询时对应的记录起始位置,组合分页查询条件pagin

     1             #########################################################
     2             ### 设置分页索引与页面大小 ###
     3             if page_size <= 0:
     4                 page_size = 10
     5             # 计算总分页数量:通过总记录数除于每页显示数量来计算总分页数量
     6             if data['records'] % page_size == 0:
     7                 page_total = data['records'] // page_size
     8             else:
     9                 page_total = data['records'] // page_size + 1
    10             # 判断页码是否超出限制,超出限制查询时会出现异常,所以将页面索引设置为最后一页
    11             if page_number < 1 or page_number > page_total:
    12                 page_number = page_total
    13             # 记录总页面数量
    14             data['total'] = page_total
    15             # 记录当前页面值
    16             data['page'] = page_number
    17             # 计算当前页面要显示的记录起始位置(limit指定的位置)
    18             record_number = (page_number - 1) * page_size
    19             # 设置查询分页条件
    20             paging = ' limit ' + str(page_size) + ' offset ' + str(record_number)
    21         #############################################################

      最后,我们组合最终查询条件,查询并输出结果

     1         ### 按条件查询数据库记录
     2         sql = "select %(column_name_list)s from %(table_name)s %(wheres)s order by %(orderby)s %(paging)s" % 
     3               {'column_name_list': column_name_list,
     4                'table_name': table_name,
     5                'wheres': wheres,
     6                'orderby': orderby,
     7                'paging': paging}
     8         result = db.execute(sql)
     9         if result:
    10             data['rows'] = result
    11             # 不需要分页查询时,直接在这里设置总记录数
    12             if page_size is None:
    13                 data['records'] = len(result)
    14 
    15     return data

      完整代码

     1    def get_list(self, column_name_list='', wheres='', page_number=None, page_size=None, orderby=None, table_name=None):
     2         """
     3         获取指定条件的数据库记录集
     4         :param column_name_list:      查询字段
     5         :param wheres:      查询条件
     6         :param page_number:   分页索引值
     7         :param page_size:    分页大小, 存在值时才会执行分页
     8         :param orderby:     排序规则
     9         :param table_name:     查询数据表,多表查询时需要设置
    10         :return: 返回记录集总数量与分页记录集
    11             {'records': 0, 'total': 0, 'page': 0, 'rows': []}
    12         """
    13         # 初始化输出参数:总记录数量与列表集
    14         data = {
    15             'records': 0,   # 总记录数
    16             'total': 0,     # 总页数
    17             'page': 1,      # 当前页面索引
    18             'rows': [],     # 查询结果(记录列表)
    19         }
    20         # 初始化查询数据表名称
    21         if not table_name:
    22             table_name = self.__table_name
    23         # 初始化查询字段名
    24         if not column_name_list:
    25             column_name_list = self.__column_name_list
    26         # 初始化查询条件
    27         if wheres:
    28             # 如果是字符串,表示该查询条件已组装好了,直接可以使用
    29             if isinstance(wheres, str):
    30                 wheres = 'where ' + wheres
    31             # 如果是list,则表示查询条件有多个,可以使用join将它们用and方式组合起来使用
    32             elif isinstance(wheres, list):
    33                 wheres = 'where ' + ' and '.join(wheres)
    34         # 初始化排序
    35         if not orderby:
    36             orderby = self.__pk_name + ' desc'
    37         # 初始化分页查询的记录区间
    38         paging = ''
    39 
    40         with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
    41             #############################################################
    42             # 判断是否需要进行分页
    43             if not page_size is None:
    44                 ### 执行sql,获取指定条件的记录总数量
    45                 sql = 'select count(1) as records from %(table_name)s %(wheres)s ' % 
    46                       {'table_name': table_name, 'wheres': wheres}
    47                 result = db.execute(sql)
    48                 # 如果查询失败或不存在指定条件记录,则直接返回初始值
    49                 if not result or result[0]['records'] == 0:
    50                     return data
    51 
    52                 # 设置记录总数量
    53                 data['records'] = result[0].get('records')
    54 
    55                 #########################################################
    56                 ### 设置分页索引与页面大小 ###
    57                 if page_size <= 0:
    58                     page_size = 10
    59                 # 计算总分页数量:通过总记录数除于每页显示数量来计算总分页数量
    60                 if data['records'] % page_size == 0:
    61                     page_total = data['records'] // page_size
    62                 else:
    63                     page_total = data['records'] // page_size + 1
    64                 # 判断页码是否超出限制,超出限制查询时会出现异常,所以将页面索引设置为最后一页
    65                 if page_number < 1 or page_number > page_total:
    66                     page_number = page_total
    67                 # 记录总页面数量
    68                 data['total'] = page_total
    69                 # 记录当前页面值
    70                 data['page'] = page_number
    71                 # 计算当前页面要显示的记录起始位置(limit指定的位置)
    72                 record_number = (page_number - 1) * page_size
    73                 # 设置查询分页条件
    74                 paging = ' limit ' + str(page_size) + ' offset ' + str(record_number)
    75             #############################################################
    76 
    77             ### 按条件查询数据库记录
    78             sql = "select %(column_name_list)s from %(table_name)s %(wheres)s order by %(orderby)s %(paging)s" % 
    79                   {'column_name_list': column_name_list,
    80                    'table_name': table_name,
    81                    'wheres': wheres,
    82                    'orderby': orderby,
    83                    'paging': paging}
    84             result = db.execute(sql)
    85             if result:
    86                 data['rows'] = result
    87                 # 不需要分页查询时,直接在这里设置总记录数
    88                 if page_size is None:
    89                     data['records'] = len(result)
    90 
    91         return data

      我们在单元测试中跑一跑,看看结果吧

     1 #!/usr/bin/evn python
     2 # coding=utf-8
     3 
     4 import unittest
     5 from common.string_helper import string
     6 from logic import product_logic
     7 
     8 class DbHelperTest(unittest.TestCase):
     9     """数据库操作包测试类"""
    10 
    11     def setUp(self):
    12         """初始化测试环境"""
    13         print('------ini------')
    14 
    15     def tearDown(self):
    16         """清理测试环境"""
    17         print('------clear------')
    18 
    19     def test(self):
    20         ##############################################
    21         # 只需要看这里,其他代码是测试用例的模板代码 #
    22         ##############################################
    23         # 实例化product表操作类ProductLogic
    24         _product_logic = product_logic.ProductLogic()
    25         result = _product_logic.get_list('*', '', 1, 2)
    26         print(result)
    27 
    28         ##############################################
    29 
    30 if __name__ == '__main__':
    31     unittest.main()

      输出结果

     1 -- -- --ini-- -- --
     2 {
     3     'records': 4,
     4     'total': 1,
     5     'page': 1,
     6     'rows': [{
     7         'content': '',
     8         'name': '名称',
     9         'place_of_origin': '',
    10         'front_cover_img': '',
    11         'code': '201808031245678',
    12         'quality_guarantee_period': '',
    13         'product_class_id': 1,
    14         'standard': '',
    15         'add_time': datetime.datetime(2018, 8, 3, 16, 51, 3),
    16         'id': 15,
    17         'is_enable': 0
    18     }, {
    19         'content': '',
    20         'name': '张三',
    21         'place_of_origin': '',
    22         'front_cover_img': '',
    23         'code': '201807251234568',
    24         'quality_guarantee_period': '',
    25         'product_class_id': 0,
    26         'standard': '',
    27         'add_time': datetime.datetime(2018, 8, 3, 0, 14, 14),
    28         'id': 14,
    29         'is_enable': 0
    30     }]
    31 }
    32 -- -- --clear-- -- --

      前面的接口我们也改造一下

     1 @get('/api/product/')
     2 def callback():
     3     """
     4     获取列表数据
     5     """
     6     # 产品分类id
     7     product_class_id = convert_helper.to_int0(web_helper.get_query('product_class_id', '产品分类id', is_check_null=False))
     8     # 类型
     9     type = web_helper.get_query('type', '类型', is_check_null=False)
    10     # 页面索引
    11     page_number = convert_helper.to_int1(web_helper.get_query('page', '', is_check_null=False))
    12     # 页面显示记录数量
    13     page_size = convert_helper.to_int0(web_helper.get_query('rows', '', is_check_null=False))
    14     # 排序字段
    15     sidx = web_helper.get_query('sidx', '', is_check_null=False)
    16     # 顺序还是倒序排序
    17     sord = web_helper.get_query('sord', '', is_check_null=False)
    18 
    19     # 设置查询条件
    20     wheres = []
    21     if product_class_id > 0:
    22         wheres.append('product_class_id=' + str(product_class_id))
    23     # 判断是否是前台提交获取数据
    24     if type != 'backstage':
    25         wheres.append('is_enable=1')
    26 
    27     # 初始化排序字段
    28     orderby = None
    29     ### 设置排序 ###
    30     if sidx:
    31         orderby = sidx + ' ' + sord
    32 
    33     # 实例化product表操作类ProductLogic
    34     _product_logic = product_logic.ProductLogic()
    35     result = _product_logic.get_list('*', wheres, page_number, page_size, orderby)
    36     if result:
    37         return web_helper.return_raise(json.dumps(result, cls=json_helper.CJsonEncoder))
    38     else:
    39         return web_helper.return_msg(-1, "查询失败")

      这样处理以后,代码看起来舒服多了

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

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

  • 相关阅读:
    [剑指Offer] 10.矩形覆盖
    [剑指Offer] 9.变态跳台阶
    [剑指Offer] 8.跳台阶
    [剑指Offer] 7.斐波那契数列
    ArtifactTransferException: Failure to transfer org.apache.openejb:javaee-api:jar:5.0-1
    -Dmaven.multiModuleProjectDirectory system property is not set. Check $M2_HO 解决办法
    java中判断list是否为空的用法
    PL/SQL快速选中一行并执行
    substring的用法
    Oracle---------sql 中取值两列中值最大的一列
  • 原文地址:https://www.cnblogs.com/EmptyFS/p/9483706.html
Copyright © 2020-2023  润新知