In this article, we’ll add some optional advanced features for our Django blog website, including a paginator, related posts, as well as a search feature.
When you add more and more posts to your blog, creating a paginator might be a good idea, since you don’t want too many posts on a single page. To do that, you need to add some extra code to the view functions. Let’s take the home view as an example. First, you must import some necessary packages:
1
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
defhome(request):
site = Site.objects.first()
categories = Category.objects.all()
tags = Tag.objects.all()
featured_post = Post.objects.filter(is_featured=True).first()
# Add Paginator page = request.GET.get("page", "") # Get the current page number posts = Post.objects.all().filter(is_published=True)
paginator = Paginator(posts, n) # Showing n post for every pagetry:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(
request,
"home.html",
{
"site": site,
"posts": posts,
"categories": categories,
"tags": tags,
"featured_post":featured_post
},
)
Line 9 to 14, here you must consider three different conditions. If the page number is an integer, return the requested page; if the page number is not an integer, return page 1; if the page number is larger than the number of pages, return the last page.
Next, you need to put the paginator in the template, along with the list of posts like this:
defpost(request, slug):
site = Site.objects.first()
requested_post = Post.objects.get(slug=slug)
categories = Category.objects.all()
tags = Tag.objects.all()
# Related Posts## Get all the tags related to this article post_tags = requested_post.tag.all()
## Filter all posts that contain tags which are related to the current post, and exclude the current post related_posts_ids = (
Post.objects.all()
.filter(tag__in=post_tags)
.exclude(id=requested_post.id)
.values_list("id")
)
related_posts = Post.objects.filter(pk__in=related_posts_ids)
return render(
request,
"post.html",
{
"site": site,
"post": requested_post,
"categories": categories,
"tags": tags,
"related_posts": related_posts,
},
)
This code is a little difficult to understand, but don’t worry, let’s analyze it line by line.
Line 3, get the requested post using the slug.
Line 9, get all the tags that belongs to the requested post.
Line 11 to 16, this is where things get tricky. First, Post.objects.all() retrieves all posts from the database. And then, filter(tag__in=post_tags) retrieves all posts that have tags which are related to the current post.
However, we have two problems. First, the current post will also be included in the query set, so we use exclude(id=requested_post.id) to exclude the current post.
The second problem, however, is not that easy to understand. Let’s consider this scenario. Here we have three posts and three tags.
Tag ID
Tag Name
1
Tag 1
2
Tag 2
3
Tag 3
Post ID
Post Name
1
Post 1
2
Post 2
3
Post 3
And the posts and tags have a many-to-many relationship to each other.
Tag ID
Post ID
1
2
1
3
1
1
2
1
2
2
2
3
3
2
Post ID
Tag ID
1
1
1
2
2
1
2
2
2
3
3
1
3
2
Let’s say our current post is post 2, that means our related tags will be 1, 2 and 3. Now, when you are using filter(tag__in=post_tags), Django will first go to tag 1, find tag 1’s related posts, which is post 2, 3 and 1, and then go to tag 2, find tag 2’s related posts, and finally move onto tag 3.
This means filter(tag__in=post_tags) will eventually return [2,3,1,1,2,3,2]. After the exclude() method, it would return [3,1,1,3]. This is still not what we want, we need to find a way to get rid of the duplicates.
This is why we need to use values_list('id') to pass the post ids to the variable related_posts_ids and then use that variable to retrieve the related posts. This way will eliminate the duplicates.
Finally, we can display the related posts in the corresponding template:
Next, you can add a search feature for your app. To create a search feature, you need a search form in the frontend, which will send the search query to the view, and the view function will retrieve the qualified records from the database, and finally return a search page that will display the result.