9、商品列表頁模塊
1、業務邏輯
-
商品模塊分為:商品列表頁 和 商品詳情頁
-
商品列表頁將所有商品按照一定的規則排序展示,用于可以從銷量、價格、上架時間和收藏數量設置商品的排序方式,并且在商品左側設置分類列表,選擇某一個分類可以篩選出對應的商品信息。
-
商品列表頁設有商品搜索功能和導航欄,網頁頂部下方劃分為3個部分:分類列表、排序設置和商品列表,當在搜索欄搜索每個商品時,商品列表會展示符合搜索條件的數據,這些數據還可以分類顯示、排序設置和分頁查詢,所以商品列表頁需實現商品關鍵字查詢、商品分類篩選、商品排序設置和分頁顯示,同時這四種方式可以任意組合并且互不干擾。
-
數據查詢適合使用GET、POST請求實現,則針對4種查詢方式分別設置請求參數n、t、s 和 p,其中n 表示商品搜索功能的關鍵字、t 表示用于查詢某一個分頁的商品、s 用設置商品的排序方式、p 用于設置商品信息的頁數。
2、功能實現
- 項目應用 commodity 下的 urls.py:
from django.urls import path
from .views import *urlpatterns = [path('', commodityView, name='commodity'), # 商品列表頁path('detail/<int:id>.html', detailView, name='detail'), # 商品詳情頁
]
- 項目應用 commodity 下的 views.py:
from django.shortcuts import render
from .models import CommodityInfos, Types
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage # 分頁def commodityView(request):'''商品列表頁的業務邏輯:param request: 請求對象:return: 渲染頁面內容'''title = '商品列表' # 頁面的標題# 查詢分類的數據typeLists = Types.objects.all() # 獲取所有的商品分類firsts_lists = Types.objects.values('firsts').distinct() # 獲取不重復的一級分類# 查詢所有商品的數據commodityInfos = CommodityInfos.objects.all()# 獲取搜索關鍵字search = request.GET.get('q', '').strip()if search: # 搜索關鍵字不為空,則查詢商品名字中包含關鍵字的數據commodityInfos = commodityInfos.filter(name__contains=search)# 獲取選擇的二級分類名稱utype = request.GET.get('t', '')if utype: # 選擇了二級分類名稱不為空,則查詢商品分類等于該名稱的數據commodityInfos = commodityInfos.filter(types=utype)# 獲取選擇的排序字段操作choice = request.GET.get('s', '')# 獲取排序字段映射(傳遞的排序操作:排序字段條件)sort_map = {'sold0': 'sold', # 銷量升序'sold1': '-sold', # 銷量降序'discount0': 'discount', # 價格升序'discount1': '-discount', # 價格降序'created0': 'created', # 最新升序'created1': '-created', # 最新降序'likes0': 'likes', # 收藏升序'likes1': '-likes', # 收藏降序}if choice and choice in sort_map: # 判斷排序字段是否存在,且在排序字段映射的字典中,在根據排序字段進行排序commodityInfos = commodityInfos.order_by(sort_map[choice])'''方式1:搜索 + 分類 + 按字段排序:參數 q + t + shttp://127.0.0.1:8003/commodity.html?q=%E5%AE%9D%E5%AE%9D&t=%E5%AE%9D%E5%AE%9D%E8%BE%85%E9%A3%9F&s=likes1方式2:分類 + 按字段排序:q='' + t + shttp://127.0.0.1:8003/commodity.html?q=&t=%E5%AE%9D%E5%AE%9D%E8%BE%85%E9%A3%9F&s=likes1'''# p 分頁功能:獲取當前頁碼數pages = request.GET.get('p', 1)# 1 創建分頁對象paginator = Paginator(commodityInfos, 6)# 2 向模板傳遞分頁對象try:pageObj = paginator.page(pages) # 對數據進行切片處理except PageNotAnInteger: # 頁面數不為整數(用戶隨機修改p的值,不是整數)pageObj = paginator.page(1)except EmptyPage: # 頁面數超過最大值pageObj = paginator.page(paginator.num_pages) # 獲取最大頁數return render(request, 'commodity.html', locals())
3、分頁功能的機制和原理
-
Django 為開發者提供了內置的分頁功能,開發者無須自己實現數據分頁功能,只需調用 Django 內置分頁功能的函數即可實現,實現數據的分頁功能需要考慮多方面的因素,分別說明如下:
- 當前用戶訪問的頁數是否存在上(下)一頁。
- 訪問的頁數是否超出頁數上限。
- 數據如何接頁截取,如何設置每頁的數據量。
-
對于上述考慮因素,Django 內置的分頁功能已提供解決方法,而且代碼的實現方式相對固定,便于開發者理解和使用。分頁功能由Paginator 類實現,我們在 PyCharm 中查看該類的定義過程
-
Paginator類一共定義了4個初始化參數和8個類方法,每個初始化參數和類方法說明如下:
- object_list:必選參數,代表需要進行分頁處理的數據,參數值可以為列表、元組或 ORM查詢的數據對象等。
- per_page:必選參數,設置每一頁的數據量,參數值必須為整型。
- orphans:可選參數,如果最后一頁的數據量小于或等于參數 orphans 的值,就將最后一頁的數據合并到前一頁的數據。比如有 23 行數據,若參數 pet_page=10、orphans=5,則數據分頁后的總頁數為 2,第一頁顯示 10 行數據,第二頁顯示 13 行數據。
- allow_empty_first_page:可選參數,是否允許第一頁為空。如果參數值為 False 并且參數
object_list為空列表,就會引發 EmptyPage 錯誤。 - validate_number():驗證當前頁數是否大于或等于 1。
- get_page():調用 validate_number() 驗證當前頁數是否有效,函數返回值調用 page()。
- page():根據當前頁數對參數 object_list 進行切片處理,獲取頁數所對應的數據信息,畫數道回值調用 _get_page()。
- _get_page():調用 Page 類,并將當前頁數和頁數所對應的數據信息傳遞給 Page類,創建當前頁數的數據對象。
- count():獲取參數 object_list的數據長度。
- num_pages():獲取分頁后的總頁數。
- page.range():將分頁后的總頁數生成可循環對象。
- _check_object_list_is_ordered():如果參數 object_list 是 ORM 查詢的數據對象,并且該數據對象的數據是無序排列的,就提示警告信息。
-
從 Paginator 類定義的 get_page()、page() 和 _get_page() 得知,三者之間存在調用關系,我
們將它們的調用關系以流程圖的形式表示
-
-
我們將 Paginator 類實例化之后,再由實例化對象調用 get_page() 即可得到Page 類的實例化對象。在源碼文件 paginator.py 中可以找到 Page 類的定義過程,它一共定義了3 個初始化參數和 7 個類方法,每個初始化參數和類方法說明如下。
- object_list:必選參數,代表已切片處理的數據對象。
- number:必選參數,代表用戶傳遞的頁數。
- paginator:必選參數,代表 Paginator 類的實例化對象。
- has_next():判斷當前頁是否存在下一頁。
- has_previous():判斷當前頁是否存在上一頁。
- has_other_pages():判斷當前頁是否存在上一頁或者下一頁。
- next_page_number():如果當前頁存在下一頁,就輸出下一頁的頁數,否則拋出 EmptyPage異常。
- previous_page_number():如果當前頁存在上一頁,就輸出上一頁的頁數,否則拋出
EmptyPage 異常。 - start_index():輸出當前頁的第一行數據在整個數據列表的位置,數據位置從1開始計算。
- end_index():輸出當前頁的最后一行數據在整個數據列表的位置,數據位置從1開始計算。
-
上述是從源碼的角度剖析分頁功能的參數和方法,下一步在 PyCharm 的 Terminal中開啟
Django的 Shell 模式,簡單地講述如何使用分頁功能,代碼如下:In [1]: from django.core.paginator import PaginatorIn [2]: # 生成數據列表In [3]: objects = [chr(x) for x in range(97, 107)]In [4]: objectsOut[4]: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']In [5]: # 將數據列表以每3個元素分為1頁In [6]: p = Paginator(objects, 3)In [7]: p.object_listOut[7]: ['a', 'b', 'c', 'd', 'e', 'f',