Louis Glenn
Louis Glenn

Reputation: 25

why the {% if post.is_liked %} doesn't work in home.html

I have passed the context of is_liked to from the PostListView to the home template, but why the if post.is_liked the statement doesn't work? I have an like_post function that when the user liked the post, the is_liked will equal to true and text will turn from not liked to liked. But why the if statements did not work(only showed not liked) in the template with no error messages? Or I have tried to change the if statements to {% post.user.is_liked %} and {% user.is_liked %}. But it still didn't work, what is the problem? thanks

models.py

class Post(models.Model):
    title = models.CharField(max_length=100)
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    likes = models.ManyToManyField(User, related_name='likes', blank=True)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})

views.py

def home(request):
    context = {
        'posts': Post.objects.all(),
    }
    return render(request, 'blog/home.html', context)


def like_post(request):   # post like
    post = get_object_or_404(Post, id=request.POST.get('post_id'))
    is_liked = False
    if post.likes.filter(id=request.user.id).exists():
        post.likes.remove(request.user)
        is_liked = False
    else:
        post.likes.add(request.user)
        is_liked = True

    return HttpResponseRedirect(post.get_absolute_url())


class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'  # <app>/<model>_<viewtype>.html
    context_object_name = 'posts'
    ordering = ['-date_posted']
    paginate_by = 10
    is_liked = False

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(PostListView, self).get_context_data()
        posts = context['posts']
        for post in posts:
            if post.likes.filter(id=self.request.user.id).exists():
                context['is_liked'] = True
            return context


class PostDetailView(DetailView):
    model = Post
    is_liked = False

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        post = context['post']
        if post.likes.filter(id=self.request.user.id).exists():
            context['is_liked'] = True
        return context

home.html

{% for post in posts %}
  <form action="{% url 'like_post' %}" method="post">
            {% csrf_token %}
            {% if post.is_liked %} #I want to get whether the post is_liked by user
                <h5>liked</h5>
            {% else %}
                <h5>not liked</h5>
            {% endif %}
    </form>
{% endfor %}

Upvotes: 1

Views: 153

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476709

You can annotate the queryset such that the Post objects that arise from this have an extra attribute .is_liked with an Exists subquery [Django-doc]:

from django.db.models import Exists, OuterRef

class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'
    context_object_name = 'posts'
    ordering = ['-date_posted']
    paginate_by = 10

    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs).annotate(
            is_liked=Exists(Post.likes.through.objects.filter(
                user_id=self.request.user.id,
                post_id=OuterRef('pk')
            ))
        )

Upvotes: 1

Related Questions