Django - Pagination - django

I'm working on To-Do App.
I have two kinds of data,
Upcoming To-do Item and
Completed To-do Items.
So, I'm filtering data in views.py as upcoming items and completed items. I want to display them on the same page using pagination. I'm getting result too but if upcoming item completed in page-1 I was unable to visit page-2 because upcoming item for page-2 was empty, but I have items on completed items. I couldn't see that.
views.py
#login_required(login_url='login')
def home(request):
todo_form = TodoForm()
current = timezone.now()
todo_items_upcoming = Todo.objects.filter(user_id=request.user, completed=False).order_by('-date_created')
todo_items_completed = Todo.objects.filter(user_id=request.user, completed=True)
pagi1 = Paginator(todo_items_upcoming, 4)
pagi2 = Paginator(todo_items_completed, 4)
page_num = request.GET.get('page')
page_num2 = request.GET.get('page')
page_obj = pagi1.get_page(page_num)
page_obj2 = pagi2.get_page(page_num2)
if request.method == "POST":
todo_form = TodoForm(request.POST)
if todo_form.is_valid():
data = todo_form.cleaned_data.get('title')
obj = Todo.objects.create(date_created=current, title=data, user_id=request.user)
context = {'todo_form':todo_form, 'page_obj':page_obj, 'page_obj2':page_obj2}
return render(request, 'todo/main.html', context)
main.html
{% extends 'todo/index.html' %}
{% load crispy_forms_tags %}
{% block content %}
<nav aria-label="...">
<ul class="pagination justify-content-center">
<!-- pagination - previous pages -->
{% if page_obj.has_previous or page_obj2.has_previous %}
<li class="page-item">
{% if page_obj.has_previous %}
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" tabindex="-1">«</a>
{% else %}
<a class="page-link" href="?page={{ page_obj2.previous_page_number }}" tabindex="-1">«</a>
{% endif %}
</li>
{% endif %}
<!-- pagination - current page -->
<li class="page-item active">
<a class="page-link" href="?page={{ page_obj.number }}">{{ page_obj.number }}</a>
</li>
<!-- pagination - Next pages -->
{% if page_obj.has_next or page_obj2.has_next %}
<li class="page-item">
{% if page_obj.has_next %}
<a class="page-link" href="?page={{ page_obj.next_page_number }}">»</a>
{% else %}
<a class="page-link" href="?page={{ page_obj2.next_page_number }}">»</a>
{% endif %}
</li>
{% endif %}
</ul>
</nav>
<div class="center-column">
<h2 class="heading" style="text-align: center;">Hello {{ request.user.username }}, Here's your ToDo List</h2>
<form action="" method="POST" id='form-create'>
{% csrf_token %}
<div style="text-align: center;">
{{ todo_form.title }}
<button type="submit" class="form-control btn btn-primary mb-3 mr-sm-2">Add Items</button>
</div>
</form>
<h4 class="heading" style="text-align: center;">Upcoming ToDo Items</h4><br>
<div class="row">
<div class="col" id="upcomItem">
<ul class="list-group" id="upcomingItems">
{% for i in page_obj %}
<li class="list-group-item list-group-item-primary mb-2" id="upcomingItem">{{ i.title }}
<div class="float-right">
<button type="submit" class="btn-sm btn-danger ml-2 mt-2 mr-2 mb-1">Delete</button>
</div>
<div class="float-right">
<button type="submit" class="btn-sm btn-success ml-2 mt-2 mr-2 mb-1" id="update_btn">Update</button>
</div>
<div class="float-right">
<button type="submit" class="btn-sm btn-dark ml-2 mt-2 mr-2 mb-1" id="completed_btn">Completed</button>
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<hr style="border-top: 1px solid #999; ">
<div class="row">
<div class="col">
<h4 class="heading" style="text-align: center;">Completed ToDo Items</h4>
<ul class="list-group" id='compItems'>
{% for i in page_obj2 %}<hr>
<li class="list-group-item list-group-item-primary mb-2" id="upcomingItem">{{ i.title }}
<div class="float-right">
<button type="submit" class="btn-sm btn-danger ml-2 mt-2 mr-2 mb-1">Delete</button>
</div>
<div class="float-right">
<button type="submit" class="btn-sm btn-success ml-2 mt-2 mr-2 mb-1" >Update</button>
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}
At first I have used or in template tags in if condition I got this error. Error Message occurs when I try to add new to-do item
I tried and operator in template tags I didn;'t even get page-2 link eventhough I have upcoming items and no extra completed items.

Use or operator as #Willem pointed. And you active link href must be {{ page_obj.number }} instead {{ page_obj.page_number }}:
<li class="page-item active">
<a class="page-link" href="?page={{ page_obj.number }}">{{ page_obj.number }}</a>
</li>
As you can see your error EmptyPage raised in your template at line 18

Your conditions should be a disjunction of the conditions of the paginators, so or instead of and:
{% if page_obj.has_previous or page_obj2.has_previous %}
…
{% endif %}
the same for .has_next. The previous and next page then should be handled by checking which page object has a next page, so:
{% if page_obj.has_next or page_obj2.has_next %}
<li class="page-item">
{% if page_obj.has_next %}
<a class="page-link" href="?page={{ page_obj.next_page_number }}">»</a>
{% else %}
<a class="page-link" href="?page={{ page_obj2.next_page_number }}">»</a>
{% endif %}
</li>
{% endif %}
That being said, I'm not sure this provides the best user experience. Perhaps it is better to make two separate paginations (so work with ?pagedone=…&pagetodo=…).

Related

django pagination with search not working

MultiValueDictKeyError at /search/
'search'
Request Method: GET
Request URL: http://127.0.0.1:8000/search/?page=2
Django Version: 4.1.5
Exception Type: MultiValueDictKeyError
Exception Value:
'search'
Exception Location: C:\Python311\Lib\site-packages\django\utils\datastructures.py, line 86, in getitem
Raised during: blogs.views.search
Python Executable: C:\Python311\python.exe
Python Version: 3.11.0
views.py
def search(request):
search = request.POST["search"]
blog_search = Blog.objects.filter(Q(title__icontains=search) | Q(description__icontains=search))
paginator = Paginator(blog_search, 5)
page_number = request.GET.get('page')
blogs = paginator.get_page(page_number)
return render(request,"frontend/searchblogs.html",{"blogs":blogs})
<div class="container">
<nav class="navbar navbar-light bg-light justify-content-end">
<form class="form-inline" method="post" action="{%url 'search' %}">
{% csrf_token %}
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" name="search" id="search" >
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</nav>
<h4 class="mt-4"></h4>
{% for blog in blogs %}
<div class="card mb-3" >
<div class="row no-gutters">
<div class="col-md-4">
<img src="{{blog.cover_img.url}}" alt="..." class="bd-placeholder-img card-img-top img-fluid">
</div>
<div class="col-md-8">
<div class="card-body">
<h4 class="card-title text-truncate">{{blog.title}}</h4>
<p class="card-text text-truncate">{{blog.description}}</p>
<div class="d-flex justify-content-between align-items-center">
<p class="text-muted">{{blog.date.date}}</p>
<p class="text-muted"><i class="fa fa-eye pl-2"></i> {{blog.view}}</p>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-end">
{% if blogs.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ blogs.previous_page_number }}">Previous</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="True">Previous</a>
</li>
{% endif %}
{% if blogs.number|add:'-4' > 1 %}
<li class="page-item"><a class="page-link" href="?page={{ blogs.number|add:'-5' }}">…</a></li>
{% endif %}
{% for i in blogs.paginator.page_range %}
{% if blogs.number == i %}
<li class="page-item active" aria-current="page">
<span class="page-link">
{{ i }}
<span class="sr-only">(current)</span>
</span>
</li>
{% elif i > blogs.number|add:'-5' and i < blogs.number|add:'5' %}
<li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if blogs.paginator.num_pages > blogs.number|add:'4' %}
<li class="page-item"><a class="page-link" href="?page={{ blogs.number|add:'5' }}">…</a></li>
{% endif %}
{% if blogs.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ blogs.next_page_number }}">Next</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="True">Next</a>
</li>
{% endif %}
</ul>
</nav>
</div>
Use:
views.py
def search(request):
search = request.GET["search"]
...
You're passing a GET request through the server (the ?page part of the URL), so you'll have to retrieve the data using request.GET['name']. You should also change your form method to GET. For a search, there isn't a good reason to use POST.
<div class="container">
<nav class="navbar navbar-light bg-light justify-content-end">
<form class="form-inline" method="GET" action="{%url 'search' %}">
{% csrf_token %}
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" name="search" id="search" >
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</nav>
...
</div>
Here's the documentation: https://docs.djangoproject.com/en/4.1/ref/request-response/#querydict-objects
Form methods: https://www.w3schools.com/tags/ref_httpmethods.asp
If you must use the POST method, refer to this question: Paginating the results of a Django forms POST request

Using django-filter generic view and paginate_by, how to build the pagination urls?

First time using django-filter, so I might be using it wrong.
I'm basically trying to mimic Django Admin filtering, using a generic FilterView, setting filterser_class and adding paginate_by
filters.py
class ProductFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='icontains', label='Filter by Title')
categories = django_filters.AllValuesFilter(
field_name='categories__name',
label="Filter by Category",
widget=django_filters.widgets.LinkWidget(attrs={'class': 'list-menu'}),
)
brand = django_filters.AllValuesFilter(
field_name='brand__short_name',
label="Filter by Brand",
widget=django_filters.widgets.LinkWidget(attrs={'class': 'list-menu'}),
)
class Meta:
model = Product
fields = ['categories', 'brand', 'name']
views.py
class ProductFilterView(FilterView):
filterset_class = ProductFilter
paginate_by = 20
product_filter.html
<section class="section-content padding-y">
<div class="container">
<div class="row">
<aside class="col-md-3">
<div class="card">
<form method="get">
{% csrf_token %}
{% for field in filter.form.visible_fields %}
<article class="filter-group">
<header class="card-header">
<a href="#" data-toggle="collapse" data-target="#collapse_1" aria-expanded="true" class="">
<i class="icon-control fa fa-chevron-down"></i>
<h6 class="title">{{ field.label_tag }}</h6>
</a>
</header>
<div class="filter-content collapse show" id="collapse_1">
<div class="card-body">
{{ field }}
</div> <!-- card-body.// -->
</div>
</article> <!-- filter-group .// -->
{% endfor %}
<input type="submit" class="btn btn-block btn-primary" value="Filter"/>
</form>
</div> <!-- card.// -->
</aside> <!-- col.// -->
<main class="col-md-9">
<header class="border-bottom mb-4 pb-3">
<div class="form-inline">
<span class="mr-md-auto">{{ filter.qs | length }} products found </span>
</div>
</header><!-- sect-heading -->
<div class="row">
{% for obj in page_obj %}
<div class="col-md-4">
<figure class="card card-product-grid">
<div class="img-wrap">
<img src="/media/{{ obj.productimage_set.first.image|default:'products/missing.png' }}">
</div> <!-- img-wrap.// -->
<figcaption class="info-wrap">
<div class="fix-height">
{{ obj.name }}
<div class="price-wrap mt-2">
<span class="price">$ {{ obj.price }}</span>
<!--<del class="price-old">$1980</del>-->
</div>
</div>
Add to cart
</figcaption>
</figure>
</div> <!-- col.// -->
{% endfor %}
</div> <!-- row end.// -->
<nav class="mt-4" aria-label="Page navigation">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page=1">First</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a></li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#">First</a></li>
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
{% endif %}
{% for page in page_obj.paginator.page_range %}
{% ifequal page page_obj.number %}
<li class="page-item active"><a class="page-link">{{ page }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ page }}">{{ page }}</a></li>
{% endifequal %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last</a></li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#">Next</a></li>
<li class="page-item disabled"><a class="page-link" href="#">Last</a></li>
{% endif %}
</ul>
</nav>
</main> <!-- col.// -->
</div>
</div> <!-- container .// -->
</section>
It almost works as intended.
But those pagination urls will reset any filtering.
I'm considering using {{ request.GET.param1 }} to rebuild all the filters, but it doesn't feel like the right way to solve this.
I've tried a solution using context processors, as described by José L. Patiño here:
How to paginate Django with other get variables?
But it leads to another problem: it's possible to reapply filtering after advancing some pages, and it results in invalid pages.
Any suggestions?

Keep django pagination and search filters when submitting form on same page

I have a view which has a list of all customers on one side, which also has working pagination and search/ filter functionality.
Where I am stuck is, if I have a form & button on my list to select a customer to view on the other side of the page, and it clears my current pagination page and any filters applied to the customer list when I run the function to pick up the selected customer.
How can I have a form action = "{% url 'some_function' %}" and keep the pagination and search.
http://localhost:8000/clients/clean?page=8&search_term=nsw
Current view:
def customer_list_clean(request, selected = None):
if selected != None:
selected = Client_Customer.objects.get(id = selected)
try:
msg = request.GET['search_term']
except:
msg = ""
clients = Client_Customer.objects.distinct().filter(Q(customer_name__contains = msg) |
Q(physical_address__street_address__contains = msg) |
Q(physical_address__suburb__suburb__contains = msg) |
Q(physical_address__suburb__post_code__contains = msg) |
Q(physical_address__suburb__region__state_abb__contains = msg)
).order_by('customer_name')
if msg:
messages.success(request, (f"Customer records filtered by: {msg}"))
found = clients.count()
if clients.filter(confirmed= False).count()>0:
messages.info(request, (f"Some customers records have not been validated!"))
#Pagination
page = request.GET.get('page', 1)
paginator = Paginator(clients, 25)
try:
clients = paginator.page(page)
except PageNotAnInteger:
clients = paginator.page(1)
except EmptyPage:
clients = paginator.page(paginator.num_pages)
if selected != None:
context ={'clients':clients, 'found':found, 'msg':msg, 'selected':selected}
else:
context ={'clients':clients, 'found':found, 'msg':msg}
return render(request, 'clients/client_list_clean.html',context)
Clients are displayed in a table (nothing special)
client_list_clean.html
{%extends 'base.html'%}
{%block body%}
<div style="margin-left: 1.2em;margin-right: 1.2em;">
<h1>Admin | Edit Customers</h1>
<h4>({{found}} records found)</h4>
<hr>
<form action = "{% url 'clients:customer_list_clean' %}" enctype="multipart/form-data" class="form-inline my-2 my-lg-0">
<!-- {% csrf_token %} -->
<input class="form-control mr-sm-2" type="search" placeholder="Search records" aria-label="Search" name = "search_term">
<button class="btn btn-outline-info my-2 my-sm-0" type="submit">Search / Clear</button>
</form>
<hr>
{%include 'client_pagination.html'%}
<div class="row">
<div class="col">
<div class="container-split" style ="height: 50vh; margin-top:3px;">
<table class = "table">
<thead>
<tr>
<th scope="col">Customer <button class="btn btn-outline-info my-2 my-sm-0" type="button">Asc</button> <button class="btn btn-outline-info my-2 my-sm-0" type="button">Desc</button></th>
<th scope="col" style="text-align:center">Type</th>
<th scope="col" style="text-align:center">Address
<button class="btn btn-outline-info my-2 my-sm-0" type="button">Asc</button> <button class="btn btn-outline-info my-2 my-sm-0" type="button">Desc</button></th>
<th scope="col" style="text-align:center">Details</th>
</tr>
<thead>
<tbody>
{% for client in clients %}
<tr>
{% if client.confirmed == False %}
<td style="background-color:#d5edf6">{{client.customer_name}}</td>
<td style="text-align:center; background-color:#d5edf6">{{client.client_type}}</td>
<!-- <td>{{client.confirmed}}</td> -->
{% for add in client.physical_address_set.all %}
<td style="text-align:right; background-color:#d5edf6">{{add}}</td>
<td style="text-align:right; background-color:#d5edf6">
<form class="" action="{% url 'clients:customer_list_clean' %}" enctype="multipart/form-data" >
<button class="btn btn-outline-info my-2 my-sm-0" type="submit">View</button>
</form>
</td>
{% endfor %}
{% else %}
<td>{{client.customer_name}}</td>
<td style="text-align:center">{{client.client_type}}</td>
{% for add in client.physical_address_set.all %}
<td style="text-align:right">{{add}}</td>
<td style="text-align:right">Something</td>
{% endfor %}
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
<hr>
</div>
</div>
<div class="col">
<div class="row">
<h3>Merge Stuff</h3>
</div>
<div class="row">
<p>some merge stuff</p>
{{selected.customer_name}}
</div>
<hr>
<div class="row">
<h3>Detail stuff</h3>
</div>
<div class="row">
<br>
<p>Some stuff</p>
</div>
</div>
</div>
</div>
{%endblock body %}
Pagination is in another template and "included" on the html page
pagination.html:
{% if clients.has_other_pages %}
<ul class="pagination">
{% if clients.number >= 11 %}
<li class="page-item"><a class="page-link" href="?page=1 &search_term={{ msg }}">FIRST</a></li>
{% endif %}
{% if clients.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ clients.previous_page_number }}&search_term={{ msg }}" aria-label="Previous">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
{% endif %}
{% if clients.number|add:'-9' > 1 %}
<li class="page-item"><a class="page-link" href="?page={{ clients.number|add:'-10' }}&search_term={{ msg }}"><em>- 10</em></a></li>
{% endif %}
{% for i in clients.paginator.page_range %}
{% if clients.number == i %}
<li class="page-item active">
<a class="page-link" href="#">{{ i }}<span class="sr-only">(current)</span></a>
</li>
{% elif i > clients.number|add:'-10' and i < clients.number|add:'10' %}
<li class="page-item"><a class="page-link" href="?page={{ i }}&search_term={{ msg }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if clients.paginator.num_pages > clients.number|add:'9' %}
<li class="page-item"><a class="page-link" href="?page={{ clients.number|add:'10' }}&search_term={{ msg }}"><em>+ 10</em></a></li>
{% endif %}
{% if clients.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ clients.next_page_number }}&search_term={{ msg }}" aria-label="Next">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}
</ul>
{% endif %}
It's all good, I worked it out.
Using an a tag instead of a form to add an additional term to the url.
<a class="btn btn-outline-info my-2 my-sm-0" href="?page={{ clients.number }}&search_term={{ msg }}&selected={{ client.id }}">View</a>
views.py
try:
print(request.GET['selected'])
except:
pass
I am yet to do something with the selected id, but I now have it in the function none the less.

Is there any way I can pass a filtered query set into Django's pagination?

view.py
def quiz(request):
question_topic = request.POST.getlist("topic_checkbox") # Retrieves list of topics selected by user
question_list = Quiz.objects.filter(Topic_name__in = question_topic) #filters out questions by topics
paginator = Paginator(question_list,1) # when i pass all the objects rather than the filtered query set it seems to work but when i paginate with the filtered queryset only the first page loads
page = request.GET.get('page')
try:
question_list = paginator.page(page)
except PageNotAnInteger:
question_list = paginator.page(1)
except EmptyPage:
question_list = paginator.page(paginator.num_pages)
return render(request,"Quiz/quiz_home.html",{"question_list":question_list})
quiz_home.html
{% block content %}
{% for q in question_list %} # loops through the filtered queryset
{% if question_list.has_next %}
<h3>Question {{q.id}}</h3>
<form method="POST" action="?page={{ question_list.next_page_number }}">{% csrf_token %}
# The form should enable me to gather the users input whilst simultaneoulsy going to the next question in the for loop. But when question sumbitted the next page is blank showing no contents
{% if q.picture %}
<img src="/media/{{q.picture}}"> <br>
{% endif %}
{{q.id}}.) </label><label id="question_text">{{q.question}}</label><br>
<input type="hidden" id= "q_id" name="q_id" value="{{q.id}}">
<input type="hidden" id= "topic" name="topic" value="{{q.topic}}">
<input type="radio" id="opt1" name="options" value="{{q.option1}}" required>{{ q.option1 }}<br>
<input type="radio" id="opt2" name="options" value="{{q.option2}}" required>{{ q.option2 }}<br>
<input type="radio" id="opt3" name="options" value="{{q.option3}}" required>{{ q.option3 }}<br>
<hr>
<input type="submit" id="mybtn" value="Submit"> #once clicked it should paginate to next page
</form>
{% else %}
<hr>
<form action="/home/">{% csrf_token %}
<input type="submit" name="End" value="End">
</form>
{% endif %}
{% endfor %}
{% endblock %}
Essentially im trying to filter out questions based on what topics the user has selected. This queryset of questions is then paginated so that each page shows one question.
I'll show you a pagination instance:
views.py
class CategoryDetail(ListView):
model = Task
template_name = 'category/category_detail.html'
context_object_name = 'task'
paginate_by = 5
def get_queryset(self):
self.category = get_object_or_404(Category,
pk=self.kwargs['pk'])
return Task.objects.filter(category=self.category).order_by('-id')
def get_context_data(self, *, object_list=None, **kwargs):
context = super(CategoryDetail, self).get_context_data(**kwargs)
self.category = get_object_or_404(Category, pk=self.kwargs['pk'])
# context['category'] = self.category
return context
category/category_detail.html
....
<div class="card-footer py-4">
{% if is_paginated %}
<nav aria-label="...">
<ul class="pagination justify-content-end mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" tabindex="-1">
<i class="fas fa-angle-left"></i>
<span class="sr-only">Previous</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">
<i class="fas fa-angle-left"></i>
<span class="sr-only">Previous</span>
</a>
</li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<a class="page-link" href="#"> {{ i }} </a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}<span class="sr-only">(current)</span></a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">
<i class="fas fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#">
<i class="fas fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
Django already has a ready-made paging structure. You can use it in your Html file you want to use.

How to insert the blog pagination on Django Blog

I trying to implement pagination on my blog using the Bootstrap.
I did comment because in this way is working but shows only one number.
I did try some different way but not working yet, I feel that is close. or not. :-)
Following my "blog.html" file.
Thank you.
<nav aria-label="Page navigation example">
<ul class="pagination pagination-lg justify-content-center">
{% if queryset.has_previous %}
<li class="page-item">
<a class="page-link" href="?{{ page_request_var }}={{ queryset.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif%}
{% comment %} <li class="page-item"><a class="page-link" href="?{{ page_request_var }}">{{ queryset.previous_page_number}}</a></li> {% endcomment %}
<li class="page-item"><a class="page-link" href="?{{ page_request_var }}">{{ queryset.number }}</a></li>
{% comment %} <li class="page-item"><a class="page-link" href="?{{ page_request_var }}">{{ queryset.next_page_number}}</a></li> {% endcomment %}
{% if queryset.has_next %}
<li class="page-item">
<a class="page-link" href="?{{ page_request_var }}={{ queryset.next_page_number }}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif%}
</ul>
</nav>
Add pagination in the html file like this:
<ul class="pagination justify-content-center">
{% if is_paginated %}
{% if page_obj.has_previous %}
<a class="btn btn-outline-dark mb-4" href="?page=1">First</a>
<a class="btn btn-outline-dark mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class="btn btn-dark mb-4" href="?page={{ num }}">{{ num }}</a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="btn btn-outline-dark mb-4" href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="btn btn-outline-dark mb-4" href="?page={{ page_obj.next_page_number }}">Next</a>
<a class="btn btn-outline-dark mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
{% endif %}
</ul>
I'm supposing you did two things.First you used Paginator in your views.py and also your queryset is not empty. Following code is completely functional and targets your template only:
paginator.html
<div class="pagination-container justify-content-center">
<ul class="pagination pagination-primary">
{% if paginator.has_previous %}
<li class="page-item arrow-margin-left">
<a class="page-link" href="?page={{ paginator.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true"><i class="fa fa-angle-double-left" aria-hidden="true"></i></span>
</a>
</li>
{% else %}
<li class="page-item arrow-margin-left">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true"><i class="fa fa-angle-double-left" aria-hidden="true"></i></span>
</a>
</li>
{% endif %}
{% for i in paginator.paginator.page_range %}
{% if paginator.number == i %}
<li class="page-item active">
<a class="page-link">{{ i }}</a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">
{{ i }}
</a>
</li>
{% endif %}
{% endfor %}
{% if paginator.has_next %}
<li class="page-item arrow-margin-right">
<a class="page-link" href="?page={{ paginator.next_page_number }}" aria-label="Next">
<span aria-hidden="true">
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
</span>
</a>
</li>
{% else %}
<li class="page-item arrow-margin-right">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
</span>
</a>
</li>
{% endif %}
</ul>
</div>
Now you can call this code wherever you need it, for example :
articles.html
<div class="pagination-container justify-content-center col-md-6">
{% include 'pages/page_articles_paginator.html' with paginator=page_articles%}
</div>
this will assign page_articles to