Why Ajax with Django is giving me double values for increase and decrease the values of the like_count?

Viewed 26

So I was making a like button with Ajax, and when I like or remove the like I need the like_count to decrease and make this in real time, but what is happening is that when I click the like button it increase or decreases 2 values and I don't know why.

Here's the code.

Models:

class Post(models.Model):
    favs = models.ManyToManyField(User, blank=True, related_name='favs')
    likes_count = models.IntegerField(default=0)

Views:

class AddLike(LoginRequiredMixin, View):
    def post(self, request, pk, *args, **kwargs):
        post = Post.objects.get(pk=pk)
 

        is_like = False

        for like in post.likes.all():
            if like == request.user:
                is_like = True
                break

        if not is_like:
            post.likes.add(request.user)
            post.likes_count = post.likes_count +1
            

        if is_like:
            post.likes.remove(request.user)
            post.likes_count = post.likes_count -1
        post.save()

        next = request.POST.get('next', '/')
        return HttpResponseRedirect(next)

HTML:

<form method="POST"  id="like-post">
                            {% csrf_token %}
                            <input type="hidden" name="next" value="{{ request.path }}">
                            <input type="hidden" name="pk" value="{{ post.pk }}">
                            <input type="hidden" name="like-count" value="{{ post.likes_count }}">

                            <button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
                            <span id="count{{ post.pk }}" >{{ post.likes_count }}</span>
                            {% if request.user in post.likes.all %}
                            
                            <i id="no-like{{ post.pk }}" style="color:gray ;display: none;" class="fa-solid fa-champagne-glasses"></i>
                            <i id="liked{{ post.pk }}" style="color:green ;" class="fa-solid fa-champagne-glasses"></i>
                            {% else %}
                            
                            <i id="liked{{ post.pk }}" style="color:green ;display: none;" class="fa-solid fa-champagne-glasses"></i>
                            <i id="no-like{{ post.pk }}" style="color:gray ; " class="fa-solid fa-champagne-glasses"></i>
                            {% endif %}
                            </button>
</form>

Ajax:

$(document).on('submit','#like-post',function(e){
        e.preventDefault();
        
        pkey = $(this).children('input')[2].value;
        
        $.ajax({
            type:'POST',
            url:"/social/post/"+pkey+"/like",
            data:{
                csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
            },
            
            success: function(response){
                
               
                console.log(pkey, count);
            },
            error: function(response){
                console.log(pkey);
            }
        });
    count = parseInt($(this).children('input')[3].value);
    if (document.getElementById("no-like" + pkey).style.display == "none"){
            document.getElementById("no-like" + pkey).style.display = "block"
            document.getElementById("liked" + pkey).style.display = 'none';     
            document.getElementById('count'+pkey).innerHTML = count - 1;
    } else {      
            document.getElementById("no-like" + pkey ).style.display = "none";
            document.getElementById("liked" + pkey).style.display = 'block';       
            document.getElementById('count'+pkey).innerHTML = count + 1;
                    
        }

    });

Pls someone help me!

1 Answers

Because you send two POST requests. First in your HTML form and second with AJAX. You didn't use any if condition in your view to recognize the ajax request

remove the method attribute from the form element and change the button type to button from submit and give an id to the button

<form id="like-post">
    {% csrf_token %}
    <input type="hidden" name="next" value="{{ request.path }}">
    <input type="hidden" name="pk" value="{{ post.pk }}">
    <input type="hidden" name="like-count" value="{{ post.likes_count }}">

    <button style="background-color: transparent; border: none; box-shadow: none;" type="button" id="submit_btn">
    <span id="count{{ post.pk }}" >{{ post.likes_count }}</span>
    {% if request.user in post.likes.all %}
    
    <i id="no-like{{ post.pk }}" style="color:gray ;display: none;" class="fa-solid fa-champagne-glasses"></i>
    <i id="liked{{ post.pk }}" style="color:green ;" class="fa-solid fa-champagne-glasses"></i>
    {% else %}
    
    <i id="liked{{ post.pk }}" style="color:green ;display: none;" class="fa-solid fa-champagne-glasses"></i>
    <i id="no-like{{ post.pk }}" style="color:gray ; " class="fa-solid fa-champagne-glasses"></i>
    {% endif %}
    </button>
</form>

in JS:

// Detect submit by click instead of submit event

$(document).on('click','#submit_btn',function(e){ //important
        e.preventDefault();
        
        pkey = $(this).children('input')[2].value;
        
        $.ajax({
            type:'POST',
            url:"/social/post/"+pkey+"/like",
            //... rest of it

Solution 2

Detect ajax requests to restrict html form request (it can be cause poor performance because you still send two post requests) in js:

$(document).on('submit','#like-post',function(e){
        e.preventDefault();
        
        pkey = $(this).children('input')[2].value;
        
        $.ajax({
            type:'POST',
            url:"/social/post/"+pkey+"/like",
            headers: {
                "X-Requested-With": "XMLHttpRequest",
            },
            data:{
                csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
            },
            
            success: function(response){
                
               
                console.log(pkey, count);
            },
            error: function(response){
                console.log(pkey);
            }
        });
    count = parseInt($(this).children('input')[3].value);
    if (document.getElementById("no-like" + pkey).style.display == "none"){
            document.getElementById("no-like" + pkey).style.display = "block"
            document.getElementById("liked" + pkey).style.display = 'none';     
            document.getElementById('count'+pkey).innerHTML = count - 1;
    } else {      
            document.getElementById("no-like" + pkey ).style.display = "none";
            document.getElementById("liked" + pkey).style.display = 'block';       
            document.getElementById('count'+pkey).innerHTML = count + 1;
                    
        }

    });

in view:

class AddLike(LoginRequiredMixin, View):
    def post(self, request, pk, *args, **kwargs):
        # request.is_ajax() is deprecated since django 3.1
        is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
        if is_ajax:
            post = Post.objects.get(pk=pk)
    
            is_like = False

            for like in post.likes.all():
                if like == request.user:
                    is_like = True
                    break

            if not is_like:
                post.likes.add(request.user)
                post.likes_count = post.likes_count +1

            if is_like:
                post.likes.remove(request.user)
                post.likes_count = post.likes_count -1
            post.save()

            next = request.POST.get('next', '/')
        return HttpResponseRedirect(next)
Related