skip to Main Content

I am attempting to make a twitter like social media feed in a Django ListView and have added an Ajax like button from the Django 3 By Example book. The like feature works perfectly in the DetailView but I cannot get it to work in the ListView. When hitting the like button in the DetailView it likes all of the posts on the page and will show 1 like, then 11110 likes, then goes up exponentially from there. The toggling of like/unlike is correct but does not reduce the count by 1; it just keeps going up. Please help!

Models.py:

class Post(models.Model):
    content = models.TextField(max_length=150, blank=True)
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    image = models.ImageField(storage=PrivateMediaStorage(), upload_to = pics_path, blank=True, null=True)
    vid = models.FileField(storage=PrivateMediaStorage(), upload_to = vids_path, blank=True, null=True)
    users_like = models.ManyToManyField(User, related_name='posts_liked', blank=True)

Views.py:

@require_POST
def post_like(request):
    post_id = request.POST.get('id') 
    action = request.POST.get('action') 
    if post_id and action:
        try:
            post = Post.objects.get(id=post_id) 
            if action == 'like':
                post.users_like.add(request.user) 
            else:
                post.users_like.remove(request.user) 
            return JsonResponse({'status':'ok'})
        except: 
            pass
    return JsonResponse({'status':'ko'})

Template aka post_list.html:

{% with total_likes=post.users_like.count users_like=post.users_like.all %}
    <div class="post-info"> 
        <div>
            <span class="count">
            <span class="total">{{ total_likes }}</span>
            like{{ total_likes|pluralize }}
            </span>
            <a href="#" data-id="{{ post.id }}" data-action="{% if request.user in users_like %}un{% endif %}like" class="like button">
            {% if request.user not in users_like %} 
            Like
            {% else %}
            Unlike
            {% endif %}
            </a>
        </div>
    </div>
{% endwith %}

jQuery:

    var csrftoken = Cookies.get('csrftoken'); 
    function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 
    }
    $.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 
        xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }   
    }
});
$(document).ready(function(){
    $('a.like').click(function(e){
        e.preventDefault();
            $.post('{% url "like" %}',
                {
                    id: $(this).data('id'), 
                    action: $(this).data('action')
                },
                function(data){
                    if (data['status'] == 'ok') 
                    {
                    var previous_action = $('a.like').data('action');
                    // toggle data-action
                    $('a.like').data('action', previous_action == 'like' ? 'unlike' : 'like');
                    // toggle link text
                    $('a.like').text(previous_action == 'like' ? 'Unlike' : 'Like');
                    // update total likes
                    var previous_likes = parseInt($('span.count .total').text()); 
                    $('span.count .total').text(previous_action == 'like' ? previous_likes + 1 : previous_likes - 1);
                } 
            }
        ); 
    });
});

2

Answers


  1. Seems like you are selecting all a.like elemnents on the DOM, same for span.count.total

    You should be more specific when selecting, ideally using id. For example in the <a>:

    <a href="#" id="{{ post.id }}" data-id="{{ post.id }}" data-action="{% if request.user in users_like %}un{% endif %}like" class="like button">
    

    Then in jQuery:

    $(document).ready(function(){
        $('a.like').click(function(e){
            e.preventDefault();
                var id = $(this).data('id');
                var action = $(this).data('action');
                $.post('{% url "like" %}',
                    {
                        id: id, 
                        action: action
                    },
                    function(data){
                        if (data['status'] == 'ok') 
                        {
                        var previous_action = $('#' + id').data('action');
                        // toggle data-action
                        $('#' + id).data('action', previous_action == 'like' ? 'unlike' : 'like');
                        // toggle link text
                        $('#' + id).text(previous_action == 'like' ? 'Unlike' : 'Like');
                        // update total likes
                        var previous_likes = parseInt($('span.count .total').text()); 
                        $('#' + id + '_total').text(previous_action == 'like' ? previous_likes + 1 : previous_likes - 1);
                    } 
                }
            ); 
        });
    });
    
    

    Same for the span elements. Keep in mind that you should use unique id for each element, so maybe you could append suffix like id="{{ post.id }}_like", id="{{ post.id }}_count", id="{{ post.id }}_total" to differentiate.

    Login or Signup to reply.
  2. AJAX CODE 
    <script>
        $("a.btn-like").click(function(e){
            e.preventDefault();
            console.log("Button CLicked");
            var id = $(this).attr('data-id');
            console.log(id);
            mydata={
                id:id
            };
            mythis=this;
            $.ajax({
                type : 'GET',
                url : '{% url "like_post" %}',
                data : mydata,
                success : function(data){
                    console.log(data.likes);
                    $('#'+id).text(data.likes);
                }
            })
        })
    

    HTML

     <div class="container mb-5 mt-4">
          {% for p in posts %}
           <div class="card mx-auto mt-4 col-sm-8">
               <p class="display-6 lead">Title : {{p.title}}</p>
               <span class='p-2'>
                <a href="" id="btn-like" name='btn-like' data-id="{{p.id}}" value='{{p.id}}' class='text-decoration-none  mb-2 btn-like' ><span id="{{p.id}}">{{p.likes}}</span> 
                    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-hand-thumbs-up mb-2" viewBox="0 0 16 16">
                        <path d="M8.864.046C7.908-.193 7.02.53 6.956 1.466c-.072 1.051-.23 2.016-.428 2.59-.125.36-.479 1.013-1.04 1.639-.557.623-1.282 1.178-2.131 1.41C2.685 7.288 2 7.87 2 8.72v4.001c0 .845.682 1.464 1.448 1.545 1.07.114 1.564.415 2.068.723l.048.03c.272.165.578.348.97.484.397.136.861.217 1.466.217h3.5c.937 0 1.599-.477 1.934-1.064a1.86 1.86 0 0 0 .254-.912c0-.152-.023-.312-.077-.464.201-.263.38-.578.488-.901.11-.33.172-.762.004-1.149.069-.13.12-.269.159-.403.077-.27.113-.568.113-.857 0-.288-.036-.585-.113-.856a2.144 2.144 0 0 0-.138-.362 1.9 1.9 0 0 0 .234-1.734c-.206-.592-.682-1.1-1.2-1.272-.847-.282-1.803-.276-2.516-.211a9.84 9.84 0 0 0-.443.05 9.365 9.365 0 0 0-.062-4.509A1.38 1.38 0 0 0 9.125.111L8.864.046zM11.5 14.721H8c-.51 0-.863-.069-1.14-.164-.281-.097-.506-.228-.776-.393l-.04-.024c-.555-.339-1.198-.731-2.49-.868-.333-.036-.554-.29-.554-.55V8.72c0-.254.226-.543.62-.65 1.095-.3 1.977-.996 2.614-1.708.635-.71 1.064-1.475 1.238-1.978.243-.7.407-1.768.482-2.85.025-.362.36-.594.667-.518l.262.066c.16.04.258.143.288.255a8.34 8.34 0 0 1-.145 4.725.5.5 0 0 0 .595.644l.003-.001.014-.003.058-.014a8.908 8.908 0 0 1 1.036-.157c.663-.06 1.457-.054 2.11.164.175.058.45.3.57.65.107.308.087.67-.266 1.022l-.353.353.353.354c.043.043.105.141.154.315.048.167.075.37.075.581 0 .212-.027.414-.075.582-.05.174-.111.272-.154.315l-.353.353.353.354c.047.047.109.177.005.488a2.224 2.224 0 0 1-.505.805l-.353.353.353.354c.006.005.041.05.041.17a.866.866 0 0 1-.121.416c-.165.288-.503.56-1.066.56z"/>
                      </svg>
                   </a>
               </span>
           </div>
          {% endfor %}
      </div>
    

    PYTHON

    def like_post(request):
        id=request.GET.get('id')
        print(id)
        post=Post.objects.get(pk=id)
        post.likes+=1
        post.save()
        return JsonResponse({'likes':post.likes})
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search