我的第一個python web開發框架(29)——定製ORM(六)

来源:https://www.cnblogs.com/EmptyFS/archive/2018/08/15/9483706.html
-Advertisement-
Play Games

在開發中,查詢操作是使用最多的,而查詢列表是其中之一,查詢列表可分為分頁查詢和不分頁查詢(它們之間多了一次總記錄數查詢),還可以分為單表查詢和多表關聯查詢,返回的結構體根據前端使用的表單框架不同而有所區別。 我們先看看,對於列表分頁查詢,在介面中是如何處理的 1 @get('/api/product ...


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

  我們先看看,對於列表分頁查詢,在介面中是如何處理的

 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         '
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 基礎知識 類,多態, ,數組和字元串,回顧,繼承,類的多態性,多態,向上轉型和向下轉型, ,數組,多維數組,字元串,字元串比較。 回顧 類的定義格式: 類的修飾符: ,`abstract final`等。 private protected public default(預設) 繼承 繼承是 ...
  • requests: 練手 雪qiu網 ...
  • 1、在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。 請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。 2、解題思路 首先選取數組中右上角的數字。如果該數字等於要查找的數字,查找過程結束;如果該數字大於要查找的數字,剔除這個數字所 ...
  • 給定一個二叉樹,檢查它是否是鏡像對稱的。 例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。 1 / \ 2 2 / \ / \ 3 4 4 3 但是下麵這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的: 1 / \ 2 2 \ \ 3 3 說明: 如果你可以運用遞歸和迭代兩 ...
  • 參考:https://blog.csdn.net/android_heng/article/details/76599302 二叉樹建立包括:根節點,左孩子,右孩子,data 定義如下: BinTree root; BinTree lChild; BinTree rChild; Object dat ...
  • 給定兩個二叉樹,編寫一個函數來檢驗它們是否相同。 如果兩個樹在結構上相同,並且節點具有相同的值,則認為它們是相同的。 示例 1: 輸入: 1 1 / \ / \ 2 3 2 3 [1,2,3], [1,2,3] 輸出: true 示例 2: 輸入: 1 1 / \ 2 2 [1,2], [1,nul ...
  • C++ 前言 c++是一種比較早的語言,具體誕生在什麼時候我就不記得了 然後進入正文 正文 Part 1 C++程式構造 C++程式構造比較簡單實現 這玩意反正Noip在考場上是可以用的,它包含了許多頭文件,所以又被稱為萬能頭文件 Ps: 本人不推薦使用萬能頭文件 雖然現在沒什麼大事發生,但是指不定 ...
  • 第一天 環境搭建和HelloWorld 1、先去下個java吧,所有語音都有自己的庫嘛,理解,我使用的是jdk1.8,雖然好像出到了9,現在大部分公司應該還是用7或者8保證版本相容吧 地址:http://www.oracle.com/technetwork/java/javase/downloads ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...