pythondjangopostcountfeed

sort feed by engagement count django


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 class="bg-white shadow rounded-md  -mx-2 lg:mx-0">

    
                            <!-- post header-->
                            <div class="flex justify-between items-center px-4 py-3">
                                <div class="flex flex-1 items-center space-x-4">
                                    <a href="#">
                                        <div class="bg-gradient-to-tr from-yellow-600 to-pink-600 p-0.5 rounded-full">  
                                            <img src="{% static 'assets/images/avatars/user.png' %}" class="bg-gray-200 border border-white rounded-full w-8 h-8">
                                        </div>
                                    </a>
                                    <span class="block capitalize font-semibold "><a href="/profile/{{ post.id }}">@{{ post.id }}  </a></span>
                                </div>
                              <div>
                                <a href="#"> <!-- <i class="icon-feather-more-horizontal text-2xl hover:bg-gray-200 rounded-full p-2 transition -mr-1 "></i> --></a>
                                <div class="bg-white w-56 shadow-md mx-auto p-2 mt-12 rounded-md text-gray-500 hidden text-base border border-gray-100  " <!-- 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 class="py-3 px-4 space-y-3"> 
                               
                                <div class="flex space-x-4 lg:font-bold">
                                    <a href="/like-post?post_id={{ post.id }}" class="flex items-center space-x-2">
                                        <div class="p-2 rounded-full text-black">
                                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="25" height="25" class="">
                                                <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 }}" class="flex items-center space-x-2">
                                        <div class="p-2 rounded-full text-black">
                                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="25" height="25" class="">
                                                <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 }}" class="flex items-center space-x-2">
                                        <div class="p-2 rounded-full text-black">
                                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="25" height="25" class="">
                                                <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('/')

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 Posts 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 ModelForms by default.