I’m doing online course CS50W by Harvard and building a web app similar to twitter.
When user sees a post I need to show the user Like or Unlike button depending on user liked it or not. There’s also a counter showing how many users liked the post so far. I am able to update the counter if the user liked or unliked the post, but I’m having problem with the code showing or hiding the buttons.
Here’s my code:
models.py
class Post(models.Model):
""" Model representing a post. """
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
no_of_likes = models.IntegerField(default=0)
def __str__(self):
return f"Post {self.id} by {self.user.username} on {self.timestamp}"
class Like(models.Model):
""" Model representing a like. """
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.user} likes {self.post}"
urls.py
path("", views.index, name="index"),
path("like/<int:post_id>", views.like, name="like"),
path("unlike/<int:post_id>", views.unlike, name="unlike"),
views.py
def index(request):
""" Home page. """
posts = Post.objects.all().order_by('-timestamp')
paginator = Paginator(posts, 5)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
likes = Like.objects.all()
# Make a list of liked posts.
liked_posts = []
try:
for like in likes:
if like.user.id == request.user.id:
liked_posts.append(like.post.id)
except:
liked_posts = []
return render(request, "network/index.html", {
"posts": posts,
"page_obj": page_obj,
"likes": likes,
"liked_posts": liked_posts,
})
@login_required
def like(request, post_id):
post = Post.objects.get(pk=post_id)
user = User.objects.get(pk=request.user.id)
like = Like.objects.create(user=user, post=post)
like.save()
# Update no of likes.
post.no_of_likes = Like.objects.filter(post=post).count()
post.save()
return JsonResponse({"message": "successfully liked", "no_of_likes": post.no_of_likes})
@login_required
def unlike(request, post_id):
post = Post.objects.get(pk=post_id)
user = User.objects.get(pk=request.user.id)
like = Like.objects.filter(user=user, post=post)
like.delete()
# Update no of likes.
post.no_of_likes = Like.objects.filter(post=post).count()
post.save()
return JsonResponse({"message": "successfully unliked", "no_of_likes": post.no_of_likes})
index.html with Javascript
{% if post.id not in liked_posts %}
<button type="button" class="btn btn-primary" id="like{{ post.id }}" onclick="like('{{ post.id }}')">Like</button>
{% else %}
<button type="button" class="btn btn-primary" id="unlike{{ post.id }}"onclick="unlike('{{ post.id }}')">Unlike</button>
{% endif %}
function like(id) {
fetch(`/like/${id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": "{{ csrf_token }}",
},
})
.then(response => response.json())
.then(data => {
//document.getElementById("like" + id).style.display = "none";
//document.getElementById("unlike" + id).style.display = "block";
document.querySelector(".no-of-likes").innerHTML = data.no_of_likes + " likes";
})
.then(() => {
document.getElementById("unlike" + id).style.display = "block";
document.getElementById("like" + id).style.display = "none";
});
}
function unlike(id) {
fetch(`/unlike/${id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": "{{ csrf_token }}",
},
})
.then(response => response.json())
.then(data => {
//document.getElementById("unlike" + id).style.display = "none";
//document.getElementById("like" + id).style.display = "block";
document.querySelector(".no-of-likes").innerHTML = data.no_of_likes + " likes";
})
.then(() => {
document.getElementById("unlike" + id).style.display = "none";
document.getElementById("like" + id).style.display = "block";
});
}
I can update the counter without refreshing the page, but it’s not the same with buttons.
This is what happening:
–> When I click Like button counter updates. I have to refresh the page to change button to Unlike. As per the specification this needs to be done asynchronously (assuming with the help of Javascript!) without reloading the page.
–> And it’s same with Unlike button too. When I click it counter updates but I have to reload the page to change the button to Like.
I want to change the buttons without reloading the page. Tried placing code blocks at different places, tried using ‘if else’ conditions but I’m still stuck here. Any help is appreciated!
Sorry for the messy Javascript code, newbie here.