この問題をしばらく調査し、このエントリから始めた後、私は複雑ですが、一般的な再利用可能なソリューションに行き着きました。
OPにはやり過ぎですが、多くの人がページ付けと検索の問題を抱えているようです。これが私の$.02です。
(Djangoの初心者なので、改善すべき点があると確信していますが、検索とページ付けをすばやく組み合わせることができます。「db」または「mdb」については無視してください。これは非常に特殊です。 Djangoデータベースの外部で生のSQLを頻繁に実行する私のアプリ。)
あらすじ:
基本的に、1つの石で2羽の鳥を殺すことができると思いました。フィルタリングされた行セットで作業していた理由は、フォームから検索を実行していたためです。
そして...フォームは、ページネーションURLを再構築するために必要な情報を提供することができます。
したがって、基本的には、通常の検索フォームを作成し、それを適切にサブクラス化されたバージョンのListViewに接続するというシステムになりました。クラスRoleListViewを参照してください。
リストビューのテンプレートも作成する必要がありますが、それを参照すると、@ pssecurity/security_list.htmlでかなり基本的なものであることがわかります。
詳細:
ページネーションに関する賢い点は、クラスKSearchListView(ListView)にあります。これは完全に一般的なもので、必要な数の検索ページで再利用できます。
メソッドget_queryset。ここで、form.doSearchを呼び出すことによってデータベースフィルタリングが実行されます。
実際のページ付けはメソッドget_context_dataにあります。このメソッドは、フォームがあるかどうかをチェックし、フォームが有効かどうかを確認してから、フォームのクリーンアップされたパラメーターを再入力してページ付けURLを操作します。
また、2つの受信URLがあります。1つはフィルタリングされていないリストで、もう1つはフィルタリングされた検索です。両方とも同じListViewにマップされます。
urls
#Roles aka PSROLEDEFN
url(r'^roles/search',
login_required(RoleListView.as_view()),
name="psroledefn_search"),
url(r'^roles/$',
# 'pssecurity.views.psroledefn_list',
login_required(RoleListView.as_view()),
name="psroledefn_list"),
#generic
class KSearchListView(ListView):
def __str__(self):
return self.__class__.__name__
__repr__ = __str__
form_class = form = None
di_hardcoded_context = {}
def setdb(self):
#specific to my app
self.db = self.rdb
def dispatch(self,*args,**kwargs):
#specific to my app
if not self.mdb:
self.mdb = MultiDb.get(self.kwargs["dbr"])
self.djangodb = self.mdb.djangodb
self.rdb = self.mdb.rdb
self.setdb()
#specific to my app
return super(KSearchListView, self).dispatch(*args,**kwargs)
def get_queryset(self,*args,**kwargs):
# logging.info("%s.get_queryset(%s,%s)" % (self,args,kwargs))
self.request.get = self.request.GET
if self.form_class:
#pagination info
#do we have a form and are we coming from it?
if self.request.method == "GET":
self.form = self.form_class(self.db, self.request.GET)
if self.form.is_valid():
logging.info("form.doSearch")
li_c = self.form.doSearch()
return li_c
else:
logging.debug("not is_valid branch")
else:
self.form = self.form_class(self.mdb.rdb)
#fetch all rows for the underlying table
return self.fetch_all()
def fetch_all(self):
#specific to my app
#you would probably use a <model>.objects.all()
return list(pssys.Pssys.fetch(self.db,self.recname))
def _override_context_data(self,context):
pass
def get_context_data(self,*args,**kwargs):
# logging.info("%s.get_context_data(%s,%s)" % (self,args,kwargs))
context = super(KSearchListView, self).get_context_data(**kwargs)
context['form'] = self.form
context["dbr"] = self.mdb.rdbname
#pagination info
#we are going to put the GET variables right back into the next/prev
url = ""
page_obj = context["page_obj"]
if self.form and self.form.is_valid() and self.form.cleaned_data:
li = [self.request.path,"?"]
#driving the url assembly from the form fields and
#using the cleaned data should be pretty safe
di = self.form.cleaned_data
for k in self.form.fields:
li.append("%s=%s" % (k,di[k]))
li.append("&")
# li = li[:-1]
url = "".join(li)
#if we didn't come in through a search form
if not url:
url = "?"
#now conditionally add the previous/next as appropriate.
#url has either a trailing ? or & at this point
kpaging_previous_url = kpaging_next_url = ""
if page_obj.has_previous():
kpaging_previous_url = context["kpaging_previous_url"] = url + "page=%s" % (page_obj.previous_page_number())
if page_obj.has_next():
kpaging_next_url = context["kpaging_next_url"] = url + "page=%s" % (page_obj.next_page_number())
logging.debug("url:%s" % (url))
logging.debug("kpaging_previous_url:%s" % (kpaging_previous_url))
logging.debug("kpaging_next_url:%s" % (kpaging_next_url))
#pickup anything the subclass has set for the context
context.update(self.di_hardcoded_context)
self._override_context_data(context)
return context
#what I need to write for every list/search page...
class RoleListView(KSearchListView):
template_name = "pssecurity/security_list.html"
paginate_by = 20
recname = "PSROLEDEFN"
form_class = Search_PSROLEDEFN
di_hardcoded_context = dict(
title="Search Roles",
header="Roles",
templatename_inst="PSROLEDEFN_list",
url_action='security:psroledefn_search')
#pagination info the forms
#generic
class SearchForm(forms.Form):
def __init__(self, db, request=None):
self.db = db
li_arg = [request] if request else []
super(forms.Form, self).__init__(*li_arg)
def __str__(self):
return self.__class__.__name__
__repr__ = __str__
#what I need to write for every list/search page...
class Search_PSROLEDEFN(SearchForm):
ROLENAME = forms.CharField(max_length=20, required=False)
DESCR = forms.CharField(max_length=32, required=False)
status_custom = ChooseStatusCustomizationField()
hasUsers = ChooseYesNoDontCareField("has Users?")
hasPermissions = ChooseYesNoDontCareField("has Permissions?")
hasPortalReferences = ChooseYesNoDontCareField("has Portal?")
def doSearch(self):
ROLENAME = self.cleaned_data["ROLENAME"]
DESCR = self.cleaned_data["DESCR"].strip()
status_custom = self.cleaned_data["status_custom"]
hasPortalReferences = self.cleaned_data["hasPortalReferences"]
hasPermissions = self.cleaned_data["hasPermissions"]
hasUsers = self.cleaned_data["hasUsers"]
#cut out a lot of code specific to my app
#you would want to do an appropriate
#<model>.objects.filter()...
returns <select from my db>
#a typical template, note that isn't even specific to an object
#refer to class RoleListView to see how the template is built.
#the actual details of the fetched objects are left to <templatename_inst>
pssecurity/security_list.html
{% block search %}
<div id="block_search">
<span>{{header}}</span>
<div class="row">
{% if form %}
<div id="search" class="well col-xs-9" >
<form class= "form-horizontal" action="{% url url_action dbr=dbr %}" method="get">
{{form.as_table}}
<input type="submit" value="search">
</form>
</div>
{% endif %}
{% endblock %}
{%block content %}
<div id = "block_content">
{% for inst in object_list %}
<div class="row">
{% include templatename_inst %}
</div>
{% endfor %}
{% include "websec/kpagination.html" %}
</div>
{%endblock %}
#generic
kpagination.html
<div class="pagination">
<span class="step-links" >
{% if li_inst.has_previous %}
<a href="?page={{ li_inst.previous_page_number }}">previous</a>
{% endif %}
<span class="current" >
Page {{ li_inst.number }} of {{ li_inst.paginator.num_pages }}.
</span>
{% if li_inst.has_next %}
<a \href="?page={{ li_inst.next_page_number }}">next</a>
{% endif %}
</span>
</div>