【问题标题】:Unable to affect other objects in ListView aside from the first one, in Django在 Django 中,除了第一个对象之外,无法影响 ListView 中的其他对象
【发布时间】:2020-12-28 19:36:24
【问题描述】:

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

models.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)

views.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

template.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>
    

【问题讨论】:

    标签: python jquery django ajax django-views


    【解决方案1】:

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

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

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

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

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

    您可以将upvote-btn 作为类而不是模板中的id,这样每个点赞按钮都有一个名为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。

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

    【讨论】:

    • 你是上帝,我的朋友。
    • 对不起,我怎么能也让它来更改所选帖子的 html?目前的代码是document.getElementById("total_upvotes").innerHTML = json['result'],它只影响第一个帖子。我试图将其更改为使用类,但无法获得结果。谢谢!
    • @cdub 也是同样的问题。每个 p 元素都有相同的 id。你可以做的是拥有段落的id = total_upvotes_for_{{post.id}},然后当你去更新它时,你可以做类似document.getElementById("total_upvotes_for_"+e.target.value).innerHTML = json['result']
    • 你是我欠你的最好的兄弟
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 2011-07-17
    • 2017-11-26
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多