Issue
I would like to sort a feed containing image posts by engagement (number_of_dislikes + number_of_likes) so that the most popular posts are at the top. But I’m not quite sure how to do it. Please see what I have so far and help me out.
There are two main issues. I tried to change it by just showing the engagement_count but it only showed the number of posts I clicked previously while the button was still there. What should I do?
Then I need to sort the feed by that number…
Thank you very much.
index.html (homepage)
{% for posts in posts %}
{% if post.image %}
{% endif %}
{% if post.number_of_likes == 0 %}
no likes
{% other than that%}
{{ post.number_of_likes }} people likes
{% endif %}
{% if post.number_of_dislikes == 0 %}
I don't like it
{% other than that%}
{{ post.number_of_dislikes }} dislikes
{% endif %}
{% if post.engagement_count == 0 %}
Nothing happened
{% other than that%}
{{post.engagement_count}}
{% endif %}
{% end for %}
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('signup', views.signup, name='signup'),
path('Upload', views.upload, name='Upload'),
path('like-post', views.like_post, name='like-post'),
path('dislike post', views.dislike_post, name='dislike post'),
path('Engagement Post', views.engagement_post, name='Engagement Post'),
]
models.py
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
User = models.CharField(max_length=100)
image = models.ImageField(upload_to='post_images')
Caption = models.TextField(max_length=100)
created_at = models.DateTimeField (default = datetime.now)
number_of_likes = models.IntegerField (default = 0)
number_of_dislikes = models.IntegerField (default = 0)
Engagement Count = models.IntegerField(null=True)#Number of Dislikes + Number of Likes
def __str__(self):
return self.user
Class LikePost(models.Model):
post_id = models.CharField(max_length=500)
Username = models.CharField(max_length=100)
def __str__(self):
return self.username
Class DislikePost(models.Model):
post_id = models.CharField(max_length=500)
Username = models.CharField(max_length=100)
def __str__(self):
return self.username
Class FollowersCount(models.Model):
follower = models.CharField(max_length=100)
User =models.CharField(max_length=100)
def __str__(self):
return self.user
views.py
def index(request):
Post = Post.objects.all()
feed=[]
Engagement_count_list = [post count,
Post.objects.annotate(count=Count('engagement_count')).order_by('-count')]
return render(request, 'index.html', {'posts': posts})
def like_post(request):
Username = request.user.username
post_id = request.GET.get('post_id')
Post = Post.objects.get(id=post_id)
new_like = LikePost.objects.create(post_id=post_id, username=username)
new_like.save()
post.number_of_likes = post.number_of_likes+1
post.save()
return redirect ('/')
def dislike_post(request):
Username = request.user.username
post_id = request.GET.get('post_id')
Post = Post.objects.get(id=post_id)
new_dislike = DislikePost.objects.create(post_id=post_id, username=username)
new_dislike.save()
post.number_of_dislikes = post.number_of_dislikes+1
post.save()
return redirect ('/')
defengagement_post (request):
post_id = request.GET.get('post_id')
Post = Post.objects.get(id=post_id)
post.engagement_count = post.number_of_likes + post.number_of_dislikes
post.save()
return redirect ('/')
Solution
I would propose to remodel this, to:
from django.conf import settings
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(upload_to='post_images')
caption = models.TextField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.caption
class LikeDislikePost(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
LIKE_DISLIKES = ((1, 'like'), (-1, 'dislike'))
like_type = models.IntegerField(choices=LIKE_DISLIKES, default=1)
def __str__(self):
return self.username
You thus create a like with like_type = 1
and a dislike with like_dislike = -1
. The like
method then thus looks like:
from django.contrib.auth.decorators import login_required
@login_required
def like_post(request):
username = request.user.username
post_id = request.GET.get('post_id')
new_like = LikePost.objects.create(post_id=post_id, user=request.user)
return redirect('/')
Then you sort the Post
s with:
from django.db.models import Count
Post.objects.alias(engagement=Count('likedislikepost')).order_by('-engagement')
There is no need to store the number of likes and dislikes in the post. This will only make it harder to keep it in sync properly.
Note: You can limit views to a view to authenticated users with the
@login_required
decorator [Django-doc].
Note: Section 9 of the HTTP protocol
specifies that requests like GET and HEAD should not have side-effects, so you
should not change entities with such requests. Normally POST, PUT, PATCH, and
DELETE requests are used for this. In that case you make a small <form>
that
will trigger a POST request, or you use some AJAX calls.
Note: Django’s DateTimeField
[Django-doc]
has a auto_now_add=…
parameter [Django-doc]
to work with timestamps. This will automatically assign the current datetime
when creating the object, and mark it as non-editable (editable=False
), such
that it does not appear in ModelForm
s by default.
Answered By – Willem Van Onsem
Answer Checked By – Marilyn (Easybugfix Volunteer)