18 Django - 自定義分頁、FBV和CBV
Django - 自定義分頁、FBV和CBV
一、自定義分頁(優勢在於能夠儲存搜尋條件)
""" 分頁元件使用示例: 1) 先取出所有資料USER_LIST 2) 例項化: obj = Pagination(request.GET.get('page',1), len(USER_LIST), request) 3) 對所有資料列表切片(即切出一頁資料): page_user_list = USER_LIST[obj.start:obj.end] 4) 返回給頁面: return render(request,'index.html',{'users': page_user_list, 'obj': obj}) 5) 模板頁面通過 {{ obj.page_html|safe }} 渲染頁碼 """ class Pagination(object): def __init__(self, current_page_num, all_count, request, per_page_num=2, pager_count=11): """ 封裝分頁相關資料 :param current_page_num: 當前訪問頁的數字 :param all_count: 分頁資料中的資料總條數 :param request: 請求物件 :param per_page_num: 每頁顯示的資料條數 :param pager_count: 最多顯示的頁碼個數 """ try: current_page_num = int(current_page_num) except Exception as e: current_page_num = 1 if current_page_num < 1: current_page_num = 1 self.current_page_num = current_page_num self.all_count = all_count self.per_page_num = per_page_num # 實際總頁碼 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count # 顯示的頁碼數 self.pager_count_half = int((pager_count - 1) / 2) # 顯示的頁碼數減1的一半 # 儲存搜尋條件 import copy self.params=copy.deepcopy(request.GET) # {"a":"1","b":"2"} @property def start(self): return (self.current_page_num - 1) * self.per_page_num @property def end(self): return self.current_page_num * self.per_page_num def page_html(self): # 如果總頁碼 <= 11: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 總頁碼 > 11 else: # 如果當前頁<=5 if self.current_page_num <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 當前頁大於5 else: # 頁碼翻到最後 if (self.current_page_num + self.pager_count_half) > self.all_pager: pager_start = self.all_pager - self.pager_count + 1 pager_end = self.all_pager + 1 else: # 正常取前五頁後五頁 pager_start = self.current_page_num - self.pager_count_half pager_end = self.current_page_num + self.pager_count_half+1 page_html_list = [] self.params["page"] = 1 first_page = '<li><a href="?%s">首頁</a></li>' % (self.params.urlencode()) page_html_list.append(first_page) if self.current_page_num <= 1: prev_page = '<li class="disabled"><a href="javascript:void(0);">上一頁</a></li>' # 第一頁的上一頁禁用 else: self.params["page"] = self.current_page_num - 1 prev_page = '<li><a href="?%s">上一頁</a></li>' % (self.params.urlencode()) page_html_list.append(prev_page) for i in range(pager_start, pager_end): self.params["page"] = i # 原搜尋條件中加上頁碼 if i == self.current_page_num: temp = '<li class="active"><a href="?%s">%s</a></li>' %(self.params.urlencode(), i) else: temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) page_html_list.append(temp) if self.current_page_num >= self.all_pager: next_page = '<li class="disabled"><a href="javascript:void(0);">下一頁</a></li>' # 最後一頁的下一頁禁用 else: self.params["page"] = self.current_page_num + 1 next_page = '<li><a href="?%s">下一頁</a></li>' % (self.params.urlencode()) page_html_list.append(next_page) self.params["page"] = self.all_pager last_page = '<li><a href="?%s">尾頁</a></li>' % (self.params.urlencode()) page_html_list.append(last_page) return ''.join(page_html_list)
分頁中關於儲存搜尋條件關鍵點總結:
我們知道,request.GET和request.POST是QueryDict型別,而且是不可更改的,因為這是使用者提交過來的原生資料,Django處於安全考慮,在原始碼中通過對request.GET和request.POST提交過來的QueryDict設定_mutable = True,即不可更改,我們在開發中如果想要修改的話,可以通過拷貝一份得到其副本(params=copy.deepcopy(request.GET) ),而副本是可以修改的(原始碼中將拷貝的副本的_mutable 設為了False,所以可以修改),根據需求改完副本後可以通過urlencode()方法將副本的QueryDict資料按照urlencoded格式(鍵值對以&連線,即a=1&b=2)拼接成字串。
二、FBV和CBV
1、FBV
FBV(function base views),就是在視圖裡使用函式處理請求,views.py中程式碼如下:
from django.shortcuts import render
def index(request): # 注意此處定義的是函式 if request.method == 'POST': print('method is :' + request.method) elif request.method == 'GET': print('method is :' + request.method) return render(request, 'index.html')
2、CBV
CBV(class base views),就是在視圖裡使用類處理請求,view.py中程式碼如下:
from django.views import View class LoginView(View): # 類要繼承View,類中函式名必須小寫 def get(self, request): print('method is :' + request.method) return render(request, 'index.html') def post(self, request): print('method is :' + request.method) return render(request, 'index.html')
3、CBV使用
1)路徑url的配置
顧名知義,CBV就是通過類的方法來呼叫,我們在url中配置為如下路徑
path('index/', views.LoginView.as_view())
這裡的LoginView是一個class 類,LoginView中的View不是必須的。一般約定成俗,會加一個View。要想使用此方法,這個路徑後面還得必須有一個as_view(),這是必須的固定格式。
2)views.py裡面函式的格式
在views裡面配置類,需要匯入一個模組View,並且使配置類繼承它,如下程式碼:
from django.views import View # 這裡必須要繼承View這個類,只有繼承了,對應url那裡的as_view()才會有這個方法 class UserView(View): def get(self, request): # get請求時執行 return HttpResponse('cbv-get') def post(self, request): return HttpResponse('cbv-post') # post請求時執行
注意:get和post的方法名,必須是小寫。因為在原始碼中,使用了request.method.lower(),通過反射來找到類中的方法!詳情請參考下面的內容
3)CBV匹配原理
這裡通過檢視View的原始碼,可以看到裡面會有很多種提交方法
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
使用ajax的時候這些方法都是可以使用的。
這種根據url來匹配方法的是通過反射方法(getattr)來做的。請求過來後先走dispatch這個方法,這個方法存在View類中。
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(),self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) request.method.lower()
將請求方式變成小寫。通過反射來找到類中對應的方法!
所有的框架,本質都是通過請求方式,反射不同的函式!
所以CBV的本質,實際還是用的FBV,只不過了類封裝而已!
4)定製dispatch
如果需要批量對方法(例如get,post等方法)做一些操作的時候,這裡我們可以手動寫一個dispatch,這個dispatch就和裝飾器的效果一樣。因為請求來的時候總是先走的dispatch,如下程式碼:
from django.views import View
class LoginView(View): def dispatch(self, request, *args, **kwargs): print('操作前的程式碼...') obj = super(LoginView, self).dispatch(request, *args, **kwargs) print('操作後的程式碼...') return obj # 這裡一定要記得return def get(self, request) return render(request, 'login.html') # 傳送到login.html def post(self, request): return HttpResponse('cbv-post')
這次我們再通過瀏覽器訪問的時候,發現不管get或者post方法,都會走print程式碼。
4、分析下面一段CBV程式碼報錯原因
from django.views import View
class LoginView(View): def dispatch(self, request, *args, **kwargs): print('dispatch...') super().dispatch(request, *args, **kwargs) def get(self, request) print('get方法...') return HttpResponse('cbv-get') def post(self, request): print('post方法...') return HttpResponse('cbv-post')
一、自定義分頁(優勢在於能夠儲存搜尋條件)
""" 分頁元件使用示例: 1) 先取出所有資料USER_LIST 2) 例項化: obj = Pagination(request.GET.get('page',1), len(USER_LIST), request) 3) 對所有資料列表切片(即切出一頁資料): page_user_list = USER_LIST[obj.start:obj.end] 4) 返回給頁面: return render(request,'index.html',{'users': page_user_list, 'obj': obj}) 5) 模板頁面通過 {{ obj.page_html|safe }} 渲染頁碼 """ class Pagination(object): def __init__(self, current_page_num, all_count, request, per_page_num=2, pager_count=11): """ 封裝分頁相關資料 :param current_page_num: 當前訪問頁的數字 :param all_count: 分頁資料中的資料總條數 :param request: 請求物件 :param per_page_num: 每頁顯示的資料條數 :param pager_count: 最多顯示的頁碼個數 """ try: current_page_num = int(current_page_num) except Exception as e: current_page_num = 1 if current_page_num < 1: current_page_num = 1 self.current_page_num = current_page_num self.all_count = all_count self.per_page_num = per_page_num # 實際總頁碼 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count # 顯示的頁碼數 self.pager_count_half = int((pager_count - 1) / 2) # 顯示的頁碼數減1的一半 # 儲存搜尋條件 import copy self.params=copy.deepcopy(request.GET) # {"a":"1","b":"2"} @property def start(self): return (self.current_page_num - 1) * self.per_page_num @property def end(self): return self.current_page_num * self.per_page_num def page_html(self): # 如果總頁碼 <= 11: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 總頁碼 > 11 else: # 如果當前頁<=5 if self.current_page_num <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 當前頁大於5 else: # 頁碼翻到最後 if (self.current_page_num + self.pager_count_half) > self.all_pager: pager_start = self.all_pager - self.pager_count + 1 pager_end = self.all_pager + 1 else: # 正常取前五頁後五頁 pager_start = self.current_page_num - self.pager_count_half pager_end = self.current_page_num + self.pager_count_half+1 page_html_list = [] self.params["page"] = 1 first_page = '<li><a href="?%s">首頁</a></li>' % (self.params.urlencode()) page_html_list.append(first_page) if self.current_page_num <= 1: prev_page = '<li class="disabled"><a href="javascript:void(0);">上一頁</a></li>' # 第一頁的上一頁禁用 else: self.params["page"] = self.current_page_num - 1 prev_page = '<li><a href="?%s">上一頁</a></li>' % (self.params.urlencode()) page_html_list.append(prev_page) for i in range(pager_start, pager_end): self.params["page"] = i # 原搜尋條件中加上頁碼 if i == self.current_page_num: temp = '<li class="active"><a href="?%s">%s</a></li>' %(self.params.urlencode(), i) else: temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) page_html_list.append(temp) if self.current_page_num >= self.all_pager: next_page = '<li class="disabled"><a href="javascript:void(0);">下一頁</a></li>' # 最後一頁的下一頁禁用 else: self.params["page"] = self.current_page_num + 1 next_page = '<li><a href="?%s">下一頁</a></li>' % (self.params.urlencode()) page_html_list.append(next_page) self.params["page"] = self.all_pager last_page = '<li><a href="?%s">尾頁</a></li>' % (self.params.urlencode()) page_html_list.append(last_page) return ''.join(page_html_list)
分頁中關於儲存搜尋條件關鍵點總結:
我們知道,request.GET和request.POST是QueryDict型別,而且是不可更改的,因為這是使用者提交過來的原生資料,Django處於安全考慮,在原始碼中通過對request.GET和request.POST提交過來的QueryDict設定_mutable = True,即不可更改,我們在開發中如果想要修改的話,可以通過拷貝一份得到其副本(params=copy.deepcopy(request.GET) ),而副本是可以修改的(原始碼中將拷貝的副本的_mutable 設為了False,所以可以修改),根據需求改完副本後可以通過urlencode()方法將副本的QueryDict資料按照urlencoded格式(鍵值對以&連線,即a=1&b=2)拼接成字串。
二、FBV和CBV
1、FBV
FBV(function base views),就是在視圖裡使用函式處理請求,views.py中程式碼如下:
from django.shortcuts import render
def index(request): # 注意此處定義的是函式 if request.method == 'POST': print('method is :' + request.method) elif request.method == 'GET': print('method is :' + request.method) return render(request, 'index.html')
2、CBV
CBV(class base views),就是在視圖裡使用類處理請求,view.py中程式碼如下:
from django.views import View class LoginView(View): # 類要繼承View,類中函式名必須小寫 def get(self, request): print('method is :' + request.method) return render(request, 'index.html') def post(self, request): print('method is :' + request.method) return render(request, 'index.html')
3、CBV使用
1)路徑url的配置
顧名知義,CBV就是通過類的方法來呼叫,我們在url中配置為如下路徑
path('index/', views.LoginView.as_view())
這裡的LoginView是一個class 類,LoginView中的View不是必須的。一般約定成俗,會加一個View。要想使用此方法,這個路徑後面還得必須有一個as_view(),這是必須的固定格式。
2)views.py裡面函式的格式
在views裡面配置類,需要匯入一個模組View,並且使配置類繼承它,如下程式碼:
from django.views import View # 這裡必須要繼承View這個類,只有繼承了,對應url那裡的as_view()才會有這個方法 class UserView(View): def get(self, request): # get請求時執行 return HttpResponse('cbv-get') def post(self, request): return HttpResponse('cbv-post') # post請求時執行
注意:get和post的方法名,必須是小寫。因為在原始碼中,使用了request.method.lower(),通過反射來找到類中的方法!詳情請參考下面的內容
3)CBV匹配原理
這裡通過檢視View的原始碼,可以看到裡面會有很多種提交方法
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
使用ajax的時候這些方法都是可以使用的。
這種根據url來匹配方法的是通過反射方法(getattr)來做的。請求過來後先走dispatch這個方法,這個方法存在View類中。
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(),self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) request.method.lower()
將請求方式變成小寫。通過反射來找到類中對應的方法!
所有的框架,本質都是通過請求方式,反射不同的函式!
所以CBV的本質,實際還是用的FBV,只不過了類封裝而已!
4)定製dispatch
如果需要批量對方法(例如get,post等方法)做一些操作的時候,這裡我們可以手動寫一個dispatch,這個dispatch就和裝飾器的效果一樣。因為請求來的時候總是先走的dispatch,如下程式碼:
from django.views import View
class LoginView(View): def dispatch(self, request, *args, **kwargs): print('操作前的程式碼...') obj = super(LoginView, self).dispatch(request, *args, **kwargs) print('操作後的程式碼...') return obj # 這裡一定要記得return def get(self, request) return render(request, 'login.html') # 傳送到login.html def post(self, request): return HttpResponse('cbv-post')
這次我們再通過瀏覽器訪問的時候,發現不管get或者post方法,都會走print程式碼。
4、分析下面一段CBV程式碼報錯原因
from django.views import View
class LoginView(View): def dispatch(self, request, *args, **kwargs): print('dispatch...') super().dispatch(request, *args, **kwargs) def get(self, request) print('get方法...') return HttpResponse('cbv-get') def post(self, request): print('post方法...') return HttpResponse('cbv-post')