10

I am adding a like functionality in my website where users can like each others posts.

I have done this successfully, however have one issue. This is checking whether the user has already liked the post, which has to be performed specifically in my HOME view.

This is so I can render my home page. To encounter this issue, I perform a .annotate() on my posts when retrieving them, and see if a user has liked a post.

I then pass this onto my home template and check if the user exists within the posts likes property.

Here's the related code.

models.py:

class Post(models.Model):
    file = models.ImageField(upload_to='images/')
    summary = models.TextField(max_length=600)
    pub_date = models.DateTimeField(auto_now=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    likes = models.ManyToManyField(User, through='Like', related_name='likes')

    def __str__(self):
        return self.user.username

    def pub_date_pretty(self):
        return self.pub_date.strftime('%b %e %Y')

    def summary_pretty(self):
        return self.summary[:50]

    @property
    def total_likes(self):
        return self.likes.count()

class Like(models.Model):
    status = models.BooleanField()
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

views.py:

def home(request):
    posts = Post.objects.all()
    liked_or_not = Post.objects.annotate(likes__user=request.user)
    return render(request, 'posts/home.html', {'posts': posts, 'liked_or_not': liked_or_not})

home.html:

{% if liked_or_not == True %}
      <a href="javascript:{document.getElementById('likepost{{ post.id }}').submit()}"><button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Unlike {{ post.total_likes }} </button></a>
{% else %}
      <a href="javascript:{document.getElementById('likepost{{ post.id }}').submit()}"><button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Like {{ post.total_likes }} </button></a>
{% endif %}
<form id="likepost{{ post.id }}" method="POST" action="{% url 'likepost' post.id %}">
  {% csrf_token%}
  <input type="hidden">
</form>
djangoninja
  • 143
  • 1
  • 1
  • 7

3 Answers3

15

For those coming here for other possibilities in debugging the error message, I had made a mistake and entered in a query analogous to

Like.objects.filter(user=request.user).values('status', flat=True)
# instead of the correct
Like.objects.filter(user=request.user).values_list('status', flat=True)
Chase
  • 321
  • 2
  • 6
4

I don't quite understand what do you want to do. If you want to check if the user liked at least one post, you can do it like this:

liked_or_not = Like.objects.filter(user=request.user).exists

If you want to check if a user liked a specific post, you can dot it like this:

liked_or_not = Likes.objects.filter(post_id=post_id, user=request.user).exists()

annotate has a different purpose. It annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet. read more here https://docs.djangoproject.com/en/2.2/ref/models/querysets/#annotate

1

I encounterd similar problem and solved using .annotate
and below is my definition of Like in models.py

class Like(models.Model):
    user = models.ForeignKey(
        "users.User", on_delete=models.CASCADE, related_name="likes"
    )
    writing = models.ForeignKey(
        "writings.Writing", on_delete=models.CASCADE, related_name="likes"
    )

in views.py

    filter_kwargs = {}
    filter_kwargs["writing"] = OuterRef("pk")
    filter_kwargs["user"] = request_user
    subquery = Like.objects.filter(**filter_kwargs)
    writing = (
        Writing.objects.annotate(user_likes=Exists(subquery))
        .get(id=id)
        )
3XBULL
  • 13
  • 2