How do I use pagination with Django class based generic ListViews?

Django

Django Problem Overview


How do I use pagination with Django 1.3?

The documentation is not very clear on this.

  • What goes to my views.py?

  • What goes to my template?

  • What goes to my URLconf file?

Django Solutions


Solution 1 - Django

I think you ask for information about using pagination with the new class based views since, with traditional function based views, it is easy to find. I found that just by setting the paginate_by variable is enough to activate the pagination. See in [Class-based generic views][1].

For example, in your views.py:

import models
from django.views.generic import ListView

class CarListView(ListView):
    model = models.Car      # shorthand for setting queryset = models.Car.objects.all()
    template_name = 'app/car_list.html'  # optional (the default is app_name/modelNameInLowerCase_list.html; which will look into your templates folder for that path and file)
    context_object_name = "car_list"    #default is object_list as well as model's_verbose_name_list and/or model's_verbose_name_plural_list, if defined in the model's inner Meta class
    paginate_by = 10  #and that's it !!

In your template (car_list.html), you can include a pagination section like this (we have some context variables available: is_paginated, page_obj, and paginator).

{# .... **Normal content list, maybe a table** .... #}
{% if car_list %}
    <table id="cars">
        {% for car in car_list %}
            <tr>
                <td>{{ car.model }}</td>
                <td>{{ car.year }}</td>
                <td><a href="/car/{{ car.id }}/" class="see_detail">detail</a></td>
            </tr>
        {% endfor %}
    </table>
    {# .... **Now the pagination section** .... #}
    {% if is_paginated %}
        <div class="pagination">
            <span class="page-links">
                {% if page_obj.has_previous %}
                    <a href="/cars?page={{ page_obj.previous_page_number }}">previous</a>
                {% endif %}
                <span class="page-current">
                    Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
                </span>
                {% if page_obj.has_next %}
                    <a href="/cars?page={{ page_obj.next_page_number }}">next</a>
                {% endif %}
            </span>
        </div>
    {% endif %}
{% else %}
    <h3>My Cars</h3>
    <p>No cars found!!! :(</p>
{% endif %}
{# .... **More content, footer, etc.** .... #}

The page to display is indicated by a GET parameter, simply adding ?page=n, to the URL.

[1]: http://docs.djangoproject.com/en/dev/ref/class-based-views/#multiple-object-mixins "django 1.3 docs: class-based-views"

Solution 2 - Django

assume, I have a class in app/models.py named FileExam(models.Model):

app/models.py

class FileExam(models.Model):
    myfile = models.FileField(upload_to='documents/%Y/%m/%d')
    date = models.DateTimeField(auto_now_add=True, blank=True)
    teacher_name = models.CharField(max_length=30)
    status = models.BooleanField(blank=True, default=False)

app/views.py

from app.models import FileExam
from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger

class FileExamListView(ListView):
    model = FileExam
    template_name = "app/exam_list.html"
    paginate_by = 10
    
    def get_context_data(self, **kwargs):
        context = super(FileExamListView, self).get_context_data(**kwargs) 
        list_exam = FileExam.objects.all()
        paginator = Paginator(list_exam, self.paginate_by)

        page = self.request.GET.get('page')

        try:
            file_exams = paginator.page(page)
        except PageNotAnInteger:
            file_exams = paginator.page(1)
        except EmptyPage:
            file_exams = paginator.page(paginator.num_pages)
            
        context['list_exams'] = file_exams
        return context

Only a little change in the get_context_data and added pagination code from django documentation here

app/templates/app/exam_list.html

normal content list

<table id="exam">
  {% for exam in list_exams %}
  <tr>
    <td>{{ exam.myfile }}</td>
    <td>{{ exam.date }}</td>
    <td>.....</td>
  </tr>
  {% endfor %}
</table>

paginate section

{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
    <li>
        <span><a href="?page={{ page_obj.previous_page_number }}">Previous</a></span>
    </li>
{% endif %}
    <li class="">
        <span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>
    </li>
{% if page_obj.has_next %}
    <li>
        <span><a href="?page={{ page_obj.next_page_number }}">Next</a></span>
    </li>
{% endif %}
</ul>
{% else %}
    <h3>Your File Exam</h3>
    <p>File not yet available</p>
{% endif %}

app/urls.py

urlpatterns = [
url(
    r'^$', views.FileExamListView.as_view(), name='file-exam-view'),
), 
... ]

Solution 3 - Django

We have 2 methods to do this.

First one is simple and just set the class field paginate_by. Nothing need we to do with get_context_data method.

The second method is a little complicated but we can gain more comprehension about pagination and customize complex pagination or several pagination. Let's see it.

It can be done in three steps.

1.Override get_context_data method of your View.

Pass page_keys and pages so that we can iterate the lists and avoid hard-coding .

def get_context_data(self, *, object_list=None, **kwargs):
	context = super().get_context_data()
	df = pd.DataFrame(list(self.model.objects.all().values()))
	ipc = df.groupby('ip')['ip'].count().sort_values(ascending=False)
	urlc = df.groupby('url')['url'].count().sort_values(ascending=False).to_dict()

	ipc = tuple(ipc.to_dict().items())
	urlc = tuple(urlc.items())

	pages = []
	page_keys = ['page1', 'page2']
	for obj, name in zip([urlc, ipc], page_keys):
	    paginator = Paginator(obj, 20)
	    page = self.request.GET.get(name)
	    page_ipc = obj
	    try:
	        page_ipc = paginator.page(page)
	    except PageNotAnInteger:
	        page_ipc = paginator.page(1)
	    except EmptyPage:
	        page_ipc = paginator.page(paginator.num_pages)
	    pages.append(page_ipc)
	    
	context['data'] = zip(pages, page_keys)
	return context

2.Customize your sub template.

We define some variables so we can iterate through the pagination list.

pagination.html

    {% if is_paginated %}
	    <ul class="pagination">
		{% if page_obj.has_previous %}
		    <li>
			<span><a href="?{{ pname }}={{ page_obj.previous_page_number }}">Previous</a></span>
		    </li>
		{% endif %}
		<li class="">
		    <span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>
		</li>
		{% if page_obj.has_next %}
		    <li>
			<span><a href="?{{ pname }}={{ page_obj.next_page_number }}">Next</a></span>
		    </li>
		{% endif %}
	    </ul>
	{% else %}
	    <h3>Your File Exam</h3>
	    <p>File not yet available</p>
	{% endif %}

3.Customize outer template.

index.html

{% for foo,name in data %}
	<div class="col-md-3 table-responsive">
	    
	        {% for k,v in foo %}
	            <tr>
	                <th>{{ forloop.counter }}</th>
	                <td>{{ k }}</td>
	                <td>{{ v }}</td>
	            </tr>
	        {% endfor %}

	    {% include 'pagination.html' with pname=name  page_obj=foo %}
	</div>
{% endfor %}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongathView Question on Stackoverflow
Solution 1 - DjangoervinView Answer on Stackoverflow
Solution 2 - DjangoYanwar SkyView Answer on Stackoverflow
Solution 3 - DjangoW.PerrinView Answer on Stackoverflow