首页 > 解决方案 > 在 Django 中,除了第一个对象之外,无法影响 ListView 中的其他对象

问题描述

我正在尝试使用 ajax 在我的 Django 站点中创建一个支持系统,因此当用户单击支持按钮时它不会刷新。在我的站点的主页中,所有帖子的列表视图中,我都能够让 ajax 和 upvote 请求工作,但仅限于顶部帖子。无论我是否点击另一个帖子的投票按钮,它只会在顶部帖子上注册效果。我哪里做错了?下面是我的代码。

模型.py

User = settings.AUTH_USER_MODEL #this is the user model

class Post(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE) #many to one relationship where many posts can be tied to one user
    content = models.TextField(blank=True, null=True) 
    date_posted = models.DateTimeField(default=timezone.now)
    image = models.ImageField(upload_to='trade_images', blank=True, null=True) 
    upvotes = models.ManyToManyField(User, blank=True, related_name='upvotes')
    total_upvotes = models.IntegerField(default='0')

    def get_absolute_url(self):
        return reverse('main:post-detail', kwargs={'pk': self.pk}) #returns the url for individual posts

    def __str__(self):
        return self.content


class Upvote(models.Model):
    user = models.ForeignKey(User, related_name='upvoted_user', on_delete=models.CASCADE)
    post = models.ForeignKey(Post, related_name='upvoted_post',  on_delete=models.CASCADE)

    def __str__(self):
        return str(self.user) + ':' + str(self.post)

视图.py

# list of all posts
class post_list_view(ListView):
    
     model = Post
     template_name = 'main/home.html'
     context_object_name = 'posts' # this is called from the html as 'for post in posts'
     ordering = ['-date_posted'] # minus to reverse the date posted, so newer posts show up on top
     paginate_by = 5 #sets pagination per page

          return context
     


def upvote(request):
     if request.POST.get('action') == 'post':
          result = ''
          id = int(request.POST.get('postid')) 
          post = get_object_or_404(Post, id=id) #grabbing the selected post using postid
          new_relation = Upvote(user=request.user, post=post) #storing the upvote relation between user and post using Upvote model arguments - 'user' and 'post'
          if post.upvotes.filter(id=request.user.id).exists(): #checking if user already has upvote relations with post by filtering user and post
               post.upvotes.remove(request.user) #remove upvote relation from post 
               post.total_upvotes -= 1 #minus 1 total_upvotes from post
               result = post.total_upvotes #storing the new total_upvotes into result
               Upvote.objects.filter(user=request.user, post=post).delete() #filtering user and post and deleting the upvote table
               post.save()
          else:
               post.upvotes.add(request.user)
               post.total_upvotes += 1
               result = post.total_upvotes
               print('create upvote')
               new_relation.save()  
               post.save()

          return JsonResponse({'result': result }) # return the new total_vote count back to html as json

模板.html

<div id="upvote-section" class="card-footer">
{% if user.is_authenticated %}
    <form action="{% url 'main:upvote-post' %}" method=POST>
        {% csrf_token %}
    <button type="submit" class="btn btn-success btn-sm" id="upvote-btn" name="post_id" value="{{post.id}}">Upvote</button>
    <button type="submit" class="btn btn-outline-success btn-sm" id="upvote-btn" name="post_id" value="{{post.id}}">Upvote</button> 
    <p id="total_upvotes">
        {{ post.total_upvotes }}
    </p>
    <a href="{% url 'main:post-detail' post.id %}" class="card-link"></i> Comment</a>
    {% if post.user == user %}
    <a href="{% url 'main:post-update' post.id %}" class="card-link">Edit</a>
    <a href="{% url 'main:post-delete' post.id %}" class="card-link" style="color:red">Delete</a>
    {% endif %}
    </form>
{% else %}
Upvotes ({{ post.total_upvotes }}) Please login to upvote or comment.
{%  endif %}  
</div>

脚本/ajax

<script>
        $(document).on('click', '#upvote-btn', function (e) {
         e.preventDefault();
         $.ajax({
           type: 'POST',
           url: '{% url "main:upvote-post" %}',
           data: {
             postid: $('#upvote-btn').val(),
             csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
             action: 'post'
           },
           success: function (json) {
             document.getElementById("total_upvotes").innerHTML = json['result']
           },
           error: function (xhr, errmsg, err) {
            console.log(err)
           }
         });
       })
     
</script>
    

标签: pythonjquerydjangoajaxdjango-views

解决方案


问题在于您的 AJAX 脚本。特别是在您的数据对象中,

data: {
         postid: $('#upvote-btn').val(),
         csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
         action: 'post'
       }

由于您选择postidusing $('#upvote-btn').val(),因此您始终会发回第一个 upvote 按钮的值。

通过使用它的 id 选择 DOM 中的任何内容,将返回与指定 id 匹配的第一个元素。这就是为什么建议您不要对 DOM 中的多个元素使用相同的 id。

既然我们知道问题出在哪里,这里有一种可能的方法来解决它:

您可以upvote-btn在模板中将 id 作为类而不是 id,这样每个upvote 按钮都有一个名为upvote-btn. 所以你的脚本变成了这样:

$(document).on('click', '.upvote-btn', function (e) {
     e.preventDefault();
     $.ajax({
       type: 'POST',
       url: '{% url "main:upvote-post" %}',
       data: {
         postid: e.target.value,
         csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
         action: 'post'
       },
       success: function (json) {
         document.getElementById("total_upvotes").innerHTML = json['result']
       },
       error: function (xhr, errmsg, err) {
        console.log(err)
       }
     });
   })

请注意,更改在脚本的第一行,我们将选择器从更改为#upvote-btn.upvote-btn因为我们之前讨论过将 upvote-btn 转换为类而不是 id。

我们还将postid您的 AJAX 调用$('#upvote-btn').val()中的e.target.value


推荐阅读