個人第一個python web開發框架(30)——定製ORM(六)

  在開發中,查詢操做是使用最多的,而查詢列表是其中之一,查詢列表可分爲分頁查詢和不分頁查詢(它們之間多了一次總記錄數查詢),還能夠分爲單表查詢和多表關聯查詢,返回的結構體根據前端使用的表單框架不一樣而有所區別。前端

  咱們先看看,對於列表分頁查詢,在接口中是如何處理的python

 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

  代碼看起來很長,有點複雜,對於這種列表分頁查詢,若是不封裝的話,開發時複製粘貼就很容易出錯,因此咱們須要從新處理才行。web

  從上面代碼能夠看到,具體功能分爲幾個部分:sql

  第一部分(9到33行)是接收並組合查詢條件,接收分頁參數和排序參數數據庫

  第二部分(37到42行)是初始化結果輸出參數json

  第三部分(44到51行)是獲取查詢總記錄數api

  第四部分(55到75行)是計算總頁數,計算當前分頁位置要顯示的記錄位置區間app

  第五部分(80到92行)是組合查詢語句,查詢並輸出結果框架

  除了產品列表這個接口,你們能夠看看產品分類列表接口,會發現兩個接口第二部分到第五部分都差很少,因此咱們封裝ORM時,能夠將這些類似部分進行處理,將它們封裝到ORM對應的方法裏。ide

 

  首先,咱們對上面代碼的分析,能夠提煉出分頁查詢方法須要有下面參數:查詢字段、查詢條件、當前分頁索引值、每頁顯示記錄數量、排序。若是是多表查詢時,咱們的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/

相關文章
相關標籤/搜索