星期一, 1月 01, 2007

(tip) 利用Generic View來對Django裡的資料分頁

Django的Generic View是非常強大而具有彈性的功能,
基本上如果應用簡單的話,
妳甚至可以不用在views.py寫任何一行程式碼,
只單單在URLconf (urls.py)進行網址改寫便可以實作出你的view,
這一點, 在Django Tutorial Part4已有詳細的說明.

不過其實撰寫Python程式碼向來不會是什麼苦差事,
所以我們真正通常關心的是要如何擴充並在view裡使用Generic View的功能.
以下我就用分頁功能來做個說明:

先假設底下這段是我們原本的view:



@login_required
def stb_list_template_view(request):
stb_list = Stb.objects.filter(stbSubGroupNum__pGroup=request.session["pGroup"])
return render_to_response('stb_list.html',{'stb_list': stb_list,})


這是一段非常標準的未使用Generic View的處理方法,
我們從Stb中取出屬於pgroup的stb_list資料,
並將之傳送到stb_list.html這個template裡. 來看看我們怎麼對這段程式碼改寫,


@login_required
def stb_list_template_view(request):
from django.views.generic import list_detail

return list_detail.object_list(
request,
queryset = Stb.objects.filter(stbSubGroupNum__pGroup=request.seession["pGroup"]),
template_object_name = "stb",
template_name = "stblist.html",
allow_empty = True,
)



疑!? 怎麼程式碼看起來更冗長了?
別急別急, 這是因為我們原先的template並沒有使用到generic views規劃的預設名稱,
否則template_object_name跟template_name這兩個參數是可以省略的,
不過別忘了python的哲學就是Explict is better than implicit,
寫長一點對於其他對generic view似懂非懂的人更好維護.
此外, 如果你原先沒有使用generic view的想法,
也可利用這兩個參數對你原有的template作最小幅度的修改.

好了, 我們還沒有做到我們原先說要作的功能, 就是替這個Stb資料列表作分頁,
那要怎麼做呢? 啊, generic view已經幫我們想好了!
請加上粗體的這行:


@login_required
def stb_list_template_view(request):
from django.views.generic import list_detail

return list_detail.object_list(
request,

paginate_by = 10,

queryset = Stb.objects.filter(stbSubGroupNum__pGroup=request.seession["pGroup"]),
template_object_name = "stb",
template_name = "stblist.html",
allow_empty = True,
)

paginate_by = 10 這行表示每十筆資料分作一頁,
這樣就完成了! django的generic view會自動幫我們在每一個template裡面加入分頁所需的參數,
以下就是說明: (以下傳入參數的說明直接來自Django官方generic view說明文件)

* results_per_page: The number of objects per page. (Same as the paginate_by parameter.)
* has_next: A boolean representing whether there's a next page.
* has_previous: A boolean representing whether there's a previous page.
* page: The current page number, as an integer. This is 1-based.
* next: The next page number, as an integer. If there's no next page, this will still be an integer representing the theoretical next-page number. This is 1-based.
* previous: The previous page number, as an integer. This is 1-based.
* pages: The total number of pages, as an integer.
* hits: The total number of objects across all pages, not just this page.

你可以自行在template編排並使用這些參數來作出你想要的分頁樣式.
(是的, 這項是可以交給前端Web Designer作的工作,
不過如果案子小或有不可抗力因素, 你可以自己動手作)

而你也可透過修改Urlconf或直接使用網址的get參數來做修改,
例如: http://yourdomain.com/obj_list/?page=3
便可觀看第三頁的內容. 這些在Django的Generic View說明文件裡面都有解釋.

可是可是, 我們還想達到更強的功能, 那要怎麼做呢?
比如說, 我們希望在某分頁找不到時, 自動導向到沒有分頁的模式,
另外我們還想自己改變錯誤訊息(而非預設的404)的網頁,等等等等...
做得到嗎???

原始的generic view是做不到的,但是一切都是Python,我們只要進行改寫就可以了.
底下列出一段我應用在真實專案上的程式碼(略有修改):


@login_required
def stb_list_template_view(request):

#customize the paginate num
if 'paginate_by' in request.GET.keys():
paginate_by = int(request.GET.get('paginate_by', 1))
request.session['paginate_by'] = paginate_by
elif 'paginate_by' in request.session:
paginate_by = request.session['paginate_by']
else:
#default behavior
#you can turn off via set paginate_by = False
paginate_by = 10
queryset = Stb.objects.filter(stbSubGroupNum__pGroup=request.session["pGroup"])

from django.views.generic import list_detail
from django.core.paginator import ObjectPaginator, InvalidPage
try:
paginator = ObjectPaginator(queryset, paginate_by)
page = int(request.GET.get('page', 1))
object_list = paginator.get_page(page - 1)
except (InvalidPage, ValueError):
paginate_by = False
except:
problem = "page 404 problem!\n"+"\n".join(map(str,sys.exc_info()))
return HttpResponse(problem)
return list_detail.object_list(
request,
paginate_by = paginate_by,
queryset = queryset,
template_object_name = "stb", # default is object, context will
template_name = "stblist.html", # default name is Stb_list.html
allow_empty = True,
extra_context = {
'serverstat' : serverstat,
'session' : request.session,
})


這是Django的好處, 魔法很少, 一切都是我們熟知的Python.
只要我們需要, 隨時可以再花點精神擴充.

B-list上有一篇延伸閱讀文章, 可以一起讀一讀.

2 則留言:

匿名 提到...

請問 Django 預設的管理介面, 使用者名單或其他 List, 有分頁功能嗎?

新增了十來筆似乎都沒有分頁,
是否有人可以指點一下?

使徒提姆 !? 提到...


預設是100筆會有分頁

詳細請見
http://www.djangoproject.com/documentation/model-api/

list_per_page

Set list_per_page to control how many items appear on each paginated admin change list page. By default, this is set to 100.

另外如果用的是django SVN版可以參考http://www.djangoproject.com/documentation/pagination/