Django ListView Page Break Using get_queryset

I am trying to learn and deal with paginated ListViews in Django, but I seem to be having trouble understanding concepts. So let me show the code - my view.py looks something like this:

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" ie send request to search server ## get back a list from the search server.. example: range(0,100) queryset = range(0,100) return queryset 

As you can see, Iโ€™m actually not filling out my request using models, but rather from my own โ€œsearch serverโ€, which returns me the results - say, in this example range (0,100).

Now I am trying to use the following in my code template:

 <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> 

and my pagination is as follows:

 {% 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> 

Now I have the following problems:

[1] Although the template seems to paginate the first set (from 0 to 14), I cannot see other values โ€‹โ€‹on subsequent pages. So, when I click on the next page, I get:

  - http://mydata:8000/search/?q=myquery/page=2 

and when I click the next page again, I get:

  - http://mydata:8000/search/?q=boots/page=2/page=2 

which is obviously wrong. I cannot see how I can generate:

  - http://mydata:8000/search/?q=boots/page=3 

[2] Even when I manually install:

  - http://mydata:8000/search/?q=boots/page=3 

Again I see values โ€‹โ€‹from 0 to 14, and not a logical set on the page: 3.

Also, this basically tells me that on every page, it seems get_queryset seems to be running, generating the first 0 to 14 values โ€‹โ€‹- this is not what I want.

As you can see, I have my own custom dataset in get_queryset, and not any interaction with the database, so I'm not sure how I can split pages into user data.

Thank you for reading my rather long post!

+4
source share
4 answers

Request parameters must be separated by & not /

 /search/?q={{searchq}}/page={{ page_obj.previous_page_number }} 

it should be:

 /search/?q={{searchq}}&page={{ page_obj.previous_page_number }} 

In addition, it is better to use a URL solution instead of hard-coding a URL, for example:

 {% url 'search' searchq page_obj.previous_page_number %} 
+4
source

For write-only purposes, the code should be:

 {% 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> 

There are two changes: 1. page_obj.number returns the actual page so that it is replaced by the variable i. 2. The for loop moves inside the obj.has_next page for clarity. If you do so, pagenumber links will only appear if there is more to this page than to this page. If you want to show it anyway: just move it.

+1
source

As a result, I got a complex, but universal reusable solution, studying this problem for some time and starting with this post.

Overkill for OP, but many people have problems with pagination and searches, so here are my $ .02.

(A little new to Django, so I'm sure there are things to improve, but I can quickly combine search and pagination. And please ignore anything about 'db' or 'mdb', this very specific to my application, which often does raw SQL outside of the Django database.)

Description:

Basically, I thought I could kill two birds with one stone. The reason I was working on a filtered rowset is because I was searching from a form.

And ... the form is capable of providing the information needed to rename the URLs of the pages.

So, basically the result was a system in which the job basically consists of creating a normal search form and then posting it with the corresponding subclass version of ListView. see class RoleListView.

I also need to create a listview template, but if you refer to it, @ pssecurity / security_list.html, you will see that it is quite simple.

Details:

Pagination characters are in the KSearchListView (ListView) class. This material is completely general, I can reuse it on as many search pages as I want.

get_queryset methods where database filtering occurs by calling the form.doSearch method.

the actual pagination is on the get_context_data method, which checks if the form exists, if it is valid, and then manipulates the URLs of the pages by re-filling it with cleared forms.

Note that there are two incoming URLs, one unfiltered, list and one filter, search). Both are mapped to the same 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> 
0
source

We can do, as shown below, in the Listview list of the django class

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/ --> 
0
source

All Articles