Django Blog #27: Getting Similar Posts

by Alex
Django Blog #27: Getting Similar Posts
Now that tags are implemented, you can do a lot with them. It’s easy to categorize posts. Material on similar topics will contain common tags. Let’s create functionality that will display similar posts based on the number of similar tags. So, when a user reads one piece of content, he will be able to recommend another, related to it thematically. To get similar posts you need to do the following steps:

  1. Get all the tags of the current post.
  2. Get all the posts with any of the same tags.
  3. Exclude the current post to avoid recommending the same post.
  4. Sort the results by the number of common tags with the current post.
  5. If there are two or more tags, recommend the most recent one.
  6. Limit the number of recommended posts.

These steps turn into a complex QuerySet, which will include the post_detail view. Open the views.py file of the blog application and add the following import at the top of it:

from django.db.models import Count

This is a Count aggregation function from Django’s ORM. It allows for aggregate counting of tags. django.db.models includes the following aggregation functions:

  • Avg: average value
  • Max: maximal value
  • Min: minimum value
  • Count: object count

Learn more about aggregation here: https://docs.djangoproject.com/en/2.0/topics/db/aggregation/. Add the following lines to the post_detail view before render() with the following indentation level:

# List of similar posts  
 post_tags_ids = post.tags.values_list('id', flat=True) 
similar_posts = post.published.filter(tags__in=post_tags_ids) \ 
   .exclude(id=post.id) 
similar_posts = similar_posts.annotate(same_tags=Count('tags')) \ 
   .order_by('-same_tags', '-publish')[:4]

This code does the following:

  1. Get a Python list with the IDs of the tags of the current post. QuerySet values_list() returns a tuple with values for the specified fields. Pass flat=True to get the list in [1, 2, 3, ...] format.
  2. We get all posts with one of these tags, not including the current one.
  3. We use the Count aggregation function to generate the same_tags field, which contains the number of common tags.
  4. Sort the results by number of common tags (in descending order) by publish to display the most recent [by publication date] posts among the first if several have the same number of common tags. We crop the results to get only the first four posts.

Add a similar_posts object to the context dictionary for render():

return return(request,  
	     'blog/post/detail.html',  
	     {'post': post,  
	     'comments': comments,  
	     'new_comment': new_comment,  
	      {' comment_form': comment_form,  
	     'similar_posts': similar_posts})

Now you need to edit the blog/post/detail.html template and add the following code before the list of post comments:

<h2>Similar posts</h2>  
{% for post in similar_posts %}  
    <p>  
        <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>  
    </p>
 {% empty %}  
    There are no similar posts yet.  
{% endfor %}

The post page should now look like this: Django Blog #27: Getting Similar PostsYou can now recommend similar posts to users. django-taggit also includes a similar_objects() manager, which you can use to get posts with common tags. A look at all the django-taggit managers can be found here: https://django-taggit.readthedocs.io/en/latest/api.html. You can also add a tag list to a post page based on the blog/post/list.html template.

Related Posts

LEAVE A COMMENT