Django custom template tags

Why my custom template tag doesn’t work?

templatetags.py:

    from django import template
    from ..models import User

    register = template.Library()


    @register.inclusion_tag('main/post_detail.html', takes_context=True)
    def get_user_liked_posts():
        request = context['request']
        user = User.objects.get(username=request.user.username)
        liked_posts = []
        for post in user.liked_posts.all():
            liked_posts.append(post.name)
        return {'liked_posts': liked_posts}

post_detail.html:

{% load static %}
{% load templatetags %}

<nav class="blog-pagination" aria-label="Pagination">
    <span id="likes_count">{{ post.likes_count }}</span>
        {% if post.name in liked_posts %}
            <button id="like_button" class="btn btn-outline-primary btn-primary text-        
                    white">Like</button>
        {% else %}
            <button id="like_button" class="btn btn-outline-primary">Like</button>
        {% endif %}
</nav>

views.py:

    class PostDetailView(DetailView):
        model = Post
        slug_field = 'url'


    class LikePostView(View):

        def post(self, request, slug):
            post = Post.objects.get(id=request.POST['id'])
            user = User.objects.get(username=request.user.username)

            if request.POST['like'] == 'true':
                post.likes_count += 1
                user.liked_posts.add(post)
            else:
                post.likes_count -= 1
                user.liked_posts.remove(post)
            user.save()
            post.save()
            return redirect('post_detail', slug)

models.py:

class Post(models.Model):
    """
    This is post model
    """
    name = models.CharField(max_length=150, blank=False)
    article = models.TextField(blank=False)
    image = models.ImageField(upload_to='uploads/', blank=True)
    likes_count = models.IntegerField(default=0)
    url = models.CharField(max_length=150, blank=False)

    def get_absolute_url(self):
        return reverse('post_detail', kwargs={'slug': self.url})

I want to check if the post is in the liked post of the current user, but it doesn’t work. It doesn’t show any errors, it just does nothing. User in my app must like or unlike posts. In models, I have many to many relationship user with the post. I want to check if the user likes this post

Answer

The problem is that you don’t even use the template tag, furthermore this is not even needed as you can simply write something like so in the template:

{% if post in request.user.liked_posts.all %}
    A Liked post
{% else %}
    Not a liked post
{% endif %}

But this is a bit inefficient as we are getting all the posts liked by the user just to check if they like some post. Also if this were in a loop with multiple posts we would be making a query for each post.

Instead we can simply annotate whether the user likes a post in the view itself using an Exists subquery [Django docs] on the through model of the many to many:

from django.db.models import Exists, OuterRef


class PostDetailView(DetailView):
    model = Post
    slug_field = 'url'
    
    def get_queryset(self):
        queryset = super().get_queryset()
        queryset = queryset.annotate(
            liked_by_user=Exists(
                User.liked_posts.through.objects.filter(
                    post_id=OuterRef("pk"),
                    user_id=self.request.user.id
                )
            )
        )
        return queryset

Now in the template we can simply write:

{% if post.liked_by_user %}
    A Liked post
{% else %}
    Not a liked post
{% endif %}

Note: Your way of saving the count similarly can simply be turned into an annotation using the Count aggregation function [Django docs]. Generally one should not store calculated attributes in a column since that might lead to inconsistent data when updating and forgetting to update the related count, etc.