Counting problem in django blog: using django ajax

夙愿已清 提交于 2020-05-17 05:58:11

问题


I created a like button for my django blog using ajax but I'm getting an error that its not counting properly, at first its 0 like in a post when i hit like it works 1 like appeared with unlike button but when i hit unlike and like again it gives 2 likes and sometimes when i unlike it show -1 like i think its jQuery problem I'm not an expert in jQuery

jQuery

$(document).ready(function() {
  function updateText(btn, newCount, verb) {
      btn.text(newCount + " " + verb)
  }

  $(".like-btn").click(function(e) {
    e.preventDefault()
    var this_ = $(this)
    var likeUrl = this_.attr("data-href")
    var likeCount = parseInt(this_.attr("data-likes")) |0
    var addLike = likeCount + 1
    var removeLike = likeCount - 1
    if (likeUrl){
       $.ajax({
        url: likeUrl,
        method: "GET",
        data: {},
        success: function(data){
          console.log(data)
          var newLikes;
          if (data.liked){
              updateText(this_, addLike, "Unlike")
          } else {
              updateText(this_, removeLike, "Like")
              // remove one like
          }

        }, error: function(error){
          console.log(error)
          console.log("error")
        }
      })
    }
  })
})

post.html

 {% if user not in post.likes.all %}
           <p><a class='like-btn' data-href='{{ object.get_api_like_url }}'
                 data-likes='{{ object.likes.all.count }}' href='{{ object.get_like_url }}'>
               {{ object.likes.all.count }} Like</a></p>
  {% else %}
            <p><a class='like-btn' data-href='{{ object.get_api_like_url }}'
                 data-likes='{{ object.likes.all.count }}' href='{{ object.get_like_url }}'>
                {{ object.likes.all.count }} Unlike</a></p>
  {% endif %}

views.py

class PostLikeToggle(RedirectView):

      def get_redirect_url(self, *args, **kwargs):
      obj = get_object_or_404(Post, pk=kwargs['pk'])
      url_ = obj.get_absolute_url()
      user = self.request.user
      if user.is_authenticated:
         if user in obj.likes.all():
            obj.likes.remove(user)
         else:
            obj.likes.add(user)
      return url_

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
from django.contrib.auth.models import User

class PostLikeApiToggle(APIView):


    authentication_classes = [authentication.SessionAuthentication]
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, pk, format=None):

        obj = get_object_or_404(Post, pk=pk)
        url_ = obj.get_absolute_url()
        user = self.request.user
        updated = False
        liked =False
        if user.is_authenticated:
           if user in obj.likes.all():
              liked = False
              obj.likes.remove(user)
           else:
               liked = True
               obj.likes.add(user)
        updated = True
        data = {
               "updated":updated,
                "liked":liked
               }

       return Response(data)

models.py

class Post(models.Model):

     title = models.CharField(max_length=200)
     author = models.ForeignKey(User,on_delete=models.CASCADE)
     likes =models.ManyToManyField(User,blank=True,related_name='post_likes')
     content = models.TextField()
     img = models.ImageField(upload_to='pics',blank=True)
     time = models.DateTimeField(default=timezone.now)

     def __str__(self):
       return self.title

    def get_absolute_url(self):
        return reverse('LoveTravel-Details', kwargs={'pk': self.pk})

    def get_like_url(self):
        return reverse('Like-Toggle', kwargs={'pk':self.pk})

   def get_api_like_url(self):
       return reverse('Like-Api-Toggle', kwargs={'pk':self.pk})

回答1:


Your issue is most likely a race condition. This means that when clicking the same button twice, your code has not yet updated the database to reflect the +1/-1, so the next click will add/remove an extra number.

There are a few ways you can combat this such as using F Expressions or inline JS counting, but the best way would probably be to just add a flag to your JS to disallow liking/disliking until your current JS request has been processed. This can be done easily by disabling the buttons when one is already clicked, and then enabling them again when the AJAX response is complete (using the complete AJAX callback).

Another few improvements you could make:

1) Remove the redundant if user.is_authenticated (since it looks like you already defined that requirements in permission_classes

2) Instead of if user in obj.likes.all(): use obj.likes.filter(likes=user), and then restructure the conditional to work based off of that




回答2:


The +2 / -1 problem is likely solved by having your API simply return the actual new like count.

  • I un-nested the javascript function by flipping the if(likeUrl) check.
  • The addLike/removeLike variables were replaced by the data.n_likes property.
  • Since this action modifies the state on the server, it's better to send it as a POST action than a GET.
function updateText(btn, newCount, verb) {
  btn.text(newCount + " " + verb);
}

$(document).ready(function() {
  $(".like-btn").click(function(e) {
    e.preventDefault();
    var this_ = $(this);
    var likeUrl = this_.attr("data-href");
    if (!likeUrl) return;
    $.ajax({
      url: likeUrl,
      method: "POST",
      data: {},
      success: function(data) {
        console.log(data);
        var newLikes;
        if (data.liked) {
          updateText(this_, data.n_likes, "Unlike");
        } else {
          updateText(this_, data.n_likes, "Like");
        }
      },
      error: function(error) {
        console.log(error);
        console.log("error");
      },
    });
  });
});

and on the backend -- you don't need DRF for this, but since it's already there --

  • Use def post instead of def get
  • No need to check if the user is authenticated; the permission class does that
  • Return the like count in the response (and drop the unnecessary updated).
class PostLikeApiToggle(APIView):

    authentication_classes = [authentication.SessionAuthentication]
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk, format=None):
        obj = get_object_or_404(Post, pk=pk)
        url_ = obj.get_absolute_url()
        user = self.request.user
        liked = False
        if obj.likes.filter(id=user.id).exists():
            obj.likes.remove(user)
        else:
            obj.likes.add(user)
            liked = True

        return Response({
            "liked": liked,
            "n_likes": obj.likes.count(),
        })

Hope this helps.



来源:https://stackoverflow.com/questions/61597981/counting-problem-in-django-blog-using-django-ajax

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!