我一直在努力学习并在Django中使用分页来处理ListViews,但我似乎在理解概念方面遇到了一些麻烦。所以,让我展示一些代码 - 我的view.py看起来像这样:
class SearchDisplayListView(ListView):
model = BlogPosts
template_name = "searchres_list.html"
paginate_by = '15'
context_object_name = "searchres"
def get_context_data(self, **kwargs):
context = super(SearchDisplayListView, self).get_context_data(**kwargs)
q = self.request.GET.get('q')
q = q.replace(" ","+")
context['searchq'] = q
return context
def get_queryset(self):
queryset = super(SearchDisplayListView, self).get_queryset()
# Get the q GET parameter
q = self.request.GET.get('q')
q = q.replace(" ","+")
if q is None:
# Return the base queryset
return queryset
# Return a filtered queryset
## do some awesome calculations using "q" i.e send request to search server
## get back a list from the search server.. example: range(0,100)
queryset = range(0,100)
return queryset
正如您所看到的,我实际上并没有使用模型填充我的查询集 - 而是使用我自己的“搜索服务器”来返回结果 - 例如在此示例范围(0,100)中。
现在,我正在尝试在模板代码中使用以下内容:
<table class="table table-hover">
<tbody>
{% for i in searchres %}
<tr>
<td><img src="http://placehold.it/140x140" class="img-polaroid"></td>
<td>{{i}}</td>
</tr>
{% endfor %}
</tbody>
</table>
我的分页看起来如下:
{% if is_paginated %}
<div class="pagination pagination-centered">
<ul>
{% if page_obj.has_previous %}
<li><a href="/search/?q={{searchq}}/page={{ page_obj.previous_page_number }}">Prev</a></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.has_next %}
<li><a href="/search/?q={{searchq}}/page={{ page_obj.number }}">{{i}}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li><a href="/search/?q={{searchq}}/page={{ page_obj.next_page_number }}">Next</a></li>
{% endif %}
</ul>
</div>
现在,我遇到以下问题:
[1]虽然模板似乎对第一组(即0到14)进行了分页,但我无法在后续页面中看到其他值。所以,当我点击下一页时,我得到:
- http://mydata:8000/search/?q=myquery/page=2
当我再次点击下一页时,我得到:
- http://mydata:8000/search/?q=boots/page=2/page=2
这显然是错误的。我无法看到如何产生:
- http://mydata:8000/search/?q=boots/page=3
[2]即使我手动设置:
- http://mydata:8000/search/?q=boots/page=3
我再次从0到14看到值,而不是页面中的逻辑集:3。
此外,这基本上告诉我,在每个页面上,get_queryset似乎都在运行,生成前0到14的值 - 这不是我想要的。
正如您所看到的,我在get_queryset中拥有自己的自定义数据集 - 而不是任何数据库交互 - 因此,我不确定如何对此自定义数据进行分页。
感谢您抽出时间阅读我相当长的帖子!
答案 0 :(得分:4)
查询参数必须用&amp;分隔。不是/
/search/?q={{searchq}}/page={{ page_obj.previous_page_number }}
应该是:
/search/?q={{searchq}}&page={{ page_obj.previous_page_number }}
此外,最佳做法是使用URL解析器而不是对网址进行硬编码,例如:
{% url 'search' searchq page_obj.previous_page_number %}
答案 1 :(得分:1)
仅供记录,代码应为:
{% if is_paginated %}
<div class="pagination pagination-centered">
<ul>
{% if page_obj.has_previous %}
<li><a href="/search/?q={{searchq}}&page={{ page_obj.previous_page_number}}">Prev</a></li>
{% endif %}
{% if page_obj.has_next %}
{% for i in paginator.page_range %}
<li><a href="/search/?q={{searchq}}&page={{ i }}">{{i}}</a></li>
{% endfor %}
<li><a href="/search/?q={{searchq}}&page={{ page_obj.next_page_number }}">Next</a></li>
{% endif %}
</ul>
</div>
有两处变化:1。page_obj.number返回实际页面,因此它被增量变量i替换。 2.为了清楚起见,for循环在page_obj.has_next内移动。如果你这样做的话,只有当提问者有超过这个页面时才会显示页面编号链接。如果你想要显示它:只需将其移出。
答案 2 :(得分:0)
在研究了这个问题一段时间后,从这个条目开始,我最终得到了一个复杂但通用的可重用解决方案。
对OP来说过度杀伤,但很多人似乎都有分页和搜索问题,所以这里是我的$ .02。
(Django的新手,所以我相信有些事情需要改进,但我现在能够很快地将搜索和分页结合起来。请忽略关于&#39; db&#39的任何事情;或者&#39; mdb&#39;,这非常特定于我的应用程序,它经常在Django数据库之外执行原始SQL。)
<强>梗概:强>
基本上,我认为我可以一石二鸟。我正在处理过滤行集的原因是因为我从表单开始搜索。
并且......表单能够提供重建分页URL所需的信息。
因此,基本上结束了一个系统,其中的工作主要包括构建一个普通的搜索表单,然后用适当的子类型ListView连接它。请参阅RoleListView类。
我还需要创建listview的模板,但是如果您参考它,@ pssecurity / security_list.html您会发现它非常基本。
<强>详细信息:强>
关于分页的智慧在类KSearchListView(ListView)上。这个东西是完全通用的,我可以在我想要的尽可能多的搜索页面上重复使用它。
方法get_queryset,通过调用form.doSearch进行数据库过滤。
实际分页是在方法get_context_data上,它检查是否有表单,如果它是有效的,然后通过使用表单的已清理参数重新填充它来操作分页URL。
另请注意,有两个传入的URL,一个未过滤,列表,一个过滤,搜索)。两者都映射到相同的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>
答案 3 :(得分:0)
我们可以在django classbased Listview中进行以下操作
views.py
try:
from urllib import urlencode
except ImportError:
from urllib.parse import urlencode
class MyListView(ListView):
# ............
def get_context_data(self, *args, **kwargs):
context = super(
GroupSubsListView, self
).get_context_data(*args, **kwargs)
query_params = self.request.GET.copy()
query_params.pop('page', None)
context['query_params'] = urlencode(query_params)
return context
template.html
<!-- pagination -->
{% if is_paginated %}
<p>showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }}</p>
<div class="text-center">
<nav>
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}&{{ query_params }}" aria-label="Previous">
Previous
</a>
</li>
{% endif %}
<li class="page-item"><a class="page-link"> Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. </a></li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}&{{ query_params }}" aria-label="Next">
Next
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
<!-- end/ -->