I would like to sort my feed containing image posts by the engagement (number_of_dislikes number_of_likes), so that the most popular post is on top. But I'm pretty unsure of how to do it. Please have a look at what I have so far and help me out. There are two main problems: one, I have the engagement-post in the html displayed in the same way as the like or dislike, which only reveals the number on click. I tried to change that by just displaying the engagement_count but it only showed the numbers of the posts I´ve clicked on before when the button was still there. How do I display an always counting number of engagement? secondly, i need to sort the feed by that count...
thanks a lot.
index.html (homepage)
{% for post in posts %}
<div >
<!-- post header-->
<div >
<div >
<a href="#">
<div >
<img src="{% static 'assets/images/avatars/user.png' %}" >
</div>
</a>
<span ><a href="/profile/{{ post.id }}">@{{ post.id }} </a></span>
</div>
<div>
<a href="#"> <!-- <i ></i> --></a>
<div <!-- uk-drop="mode: hover;pos: top-right">
</li>
</ul>
</div>
</div>
</div>
{% if post.image %}
<div uk-lightbox>
<a href="{{ post.image.url}}">
<img src="{{post.image.url}}" alt="">
</a>
</div>
{% endif %}
<div >
<div >
<a href="/like-post?post_id={{ post.id }}" >
<div >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="25" height="25" >
<path d="M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z" />
</svg>
{% if post.number_of_likes == 0 %}
<p>no likes</p>
{% else%}
<p>liked by {{ post.number_of_likes }} user</p>
{% endif %}
</div>
</a>
<a href="/dislike-post?post_id={{ post.id }}" >
<div >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="25" height="25" >
<path d="M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z" />
</svg>
{% if post.number_of_dislikes == 0 %}
<p>no dislikes</p>
{% else%}
<p>disliked by {{ post.number_of_dislikes }} user</p>
{% endif %}
</div>
</a>
<a href="/engagement-post?post_id={{ post.id }}" >
<div >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="25" height="25" >
<path d="M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z" />
</svg>
{% if post.engagement_count == 0 %}
<p>nothing happened</p>
{% else%}
<p>{{ post.engagement_count}} </p>
{% endif %}
</div>
</a>
</div>
</div>
{% endfor %}
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):
posts= Post.objects.all()
feed=[]
engagement_count_list = [posts,
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('/')
def engagement_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('/')
CodePudding user response:
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 aauto_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 inModelForm
s by default.
CodePudding user response:
Out of some other reasons I need the interaction stored in each post. But the solution was in the answer before. I mixed that approach with mine.
def index(request):
posts = Post.objects.all().order_by('interaction_count')
count_posts = Post.objects.count()
return render(request, 'index.html', {'posts': posts, 'count_posts':count_posts})
def like_post(request):
post_id = request.GET.get('post_id')
post = Post.objects.get(id=post_id)
new_like = LikePost.objects.create(post_id=post_id)
new_like.save()
post.number_of_likes = post.number_of_likes 1
new_interaction = InteractionPost.objects.create(post_id=post_id)
new_interaction.save()
post.interaction_count = post.interaction_count 1
post.save()
return redirect('/')
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
num_post = models.IntegerField(default=0)
image = models.ImageField(upload_to=post_images)
caption = models.TextField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
number_of_likes = models.IntegerField(default=0)
number_of_dislikes = models.IntegerField(default=0)
interaction_count = models.IntegerField(default=0)
def __str__(self):
return self.caption
thank you for your help.