Prerequisities
Use this to setup a base project:
Django - Blog App Tutorial (Python)
Blog View
Edit blog/views.py and add these lines:
from django.views.generic import DetailView
from django.views.generic import ListView # here
from blog.models import Post
# here:
class BlogView(ListView):
model = Post
paginate_by = 5
template_name = 'blog/index.html'
context_object_name = 'posts'
class PostDetailView(DetailView):
model = Post
Blog URLs
Edit blog/urls.py and add these lines:
from django.urls import path
from blog.views import PostDetailView, BlogView # import BlogView
app_name = 'blog'
urlpatterns = [
path('', BlogView.as_view(), name='blog'), # here
path('<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
]
Blog Index Template
Create templates/blog/index.html and add these lines to it:
<ul>
{% for post in posts %}
<li>
<a href="{{ post.get_absolute_url}}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
{% if page_obj.paginator.num_pages > 1 %}
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
{% endif %}
{% endif %}
Create More Posts
Run these commands on the command line:
python manage.py shell
>>> from blog.models import Post
>>> for i in range(15):
... content = "Lorem ipsum %s" % i
... slug = "lorem-ipsum-%s" % i
... Post.objects.create(title=content, body=content, slug=slug)
Hit [Enter] after range(15):
and [Tab] before rest of the lines.
Visit /blog/ and you should see this:
Function-Based Approach
For function-based views, you can use the Paginator
class.
Edit blog/views.py and add these lines:
from django.shortcuts import render # here
from django.core.paginator import Paginator # here
from django.views.generic import DetailView
from django.views.generic import ListView
from blog.models import Post
class BlogView(ListView):
model = Post
paginate_by = 5
template_name = 'blog/index.html'
context_object_name = 'posts'
class PostDetailView(DetailView):
model = Post
# here
def index2(request):
posts = Post.objects.all()
paginator = Paginator(posts, 5)
page = request.GET.get('page')
posts = paginator.get_page(page)
return render(request,
'blog/index2.html',
{'posts': posts})
Edit blog/urls.py and add these lines:
from django.urls import path
from blog.views import PostDetailView, BlogView
from blog.views import index2 # here
app_name = 'blog'
urlpatterns = [
path('', BlogView.as_view(), name='blog'),
path('index2/', index2, name='index2'), # here
path('<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
]
Create templates/blog/index2.html and add these lines to it:
<ul>
{% for post in posts %}
<li>
<a href="{{ post.get_absolute_url}}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
{% if posts.paginator.num_pages > 1 %}
{% if posts.has_previous %}
<a href="?page={{ posts.previous_page_number }}">previous</a>
{% endif %}
<span>Page {{ posts.number }} of {{ posts.paginator.num_pages }}</span>
{% if posts.has_next %}
<a href="?page={{ posts.next_page_number }}">next</a>
{% endif %}
{% endif %}
In this function-based example, we use the posts
object to access the pagination data, not page_obj
.
Visit /blog/index2/.
Blue Theme
Create templates/blue_theme.html and add these lines to it:
<!doctype html>
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<style>
.pagination {
margin-top: 1em;
}
.pagination a {
text-decoration: none;
}
.pagination-number {
padding: 0.5em 0.8em;
border-radius: 2px;
color: #fff;
background-color: #6D85C7;
}
.pagination-number:hover,
.pagination-current {
background-color: #3354AA;
}
.pagination-action {
margin: 0 0.1em;
display: inline-block;
padding: 0.5em 0.5em;
color: #B9B9B9;
font-size: 1.3em;
}
.pagination-action:hover,
.pagination-previous,
.pagination-next {
color: #3354AA;
}
</style>
<title>Blue Pagination Theme</title>
</head>
<body>
<ul>
{% for post in posts %}
<li>
<a href="{{ post.get_absolute_url}}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
<div class="pagination">
{% if page_obj.has_previous %}
<a class="pagination-action" href="?page=1">
<i class="fa fa-angle-double-left" aria-hidden="true"></i> </a>
<a class="pagination-action" href="?page={{ page_obj.previous_page_number }}">
<i class="fa fa-angle-left" aria-hidden="true"></i>
</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<span class="pagination-number pagination-current">{{ num }}</span>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="pagination-number" href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="pagination-action" href="?page={{ page_obj.next_page_number }}">
<i class="fa fa-angle-right" aria-hidden="true"></i>
</a>
<a class="pagination-action" href="?page={{ page_obj.paginator.num_pages }}">
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
</a>
{% endif %}
</div>
</body>
</html>
Edit blog/views.py and use the new template instead:
from django.shortcuts import render
from django.core.paginator import Paginator
from django.views.generic import DetailView
from django.views.generic import ListView
from blog.models import Post
class BlogView(ListView):
model = Post
paginate_by = 5
# template_name = 'blog/index.html'
template_name = 'blog/blue_theme.html' # here
context_object_name = 'posts'
class PostDetailView(DetailView):
model = Post
def index2(request):
posts = Post.objects.all()
paginator = Paginator(posts, 5)
page = request.GET.get('page')
posts = paginator.get_page(page)
return render(request,
'blog/index2.html',
{'posts': posts})
Visit /blog/ and you should see this: