You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中Ajax点赞按钮异常:循环内外均出问题求助

问题描述

原本实现了常规点赞按钮,运行正常但页面刷新体验不佳,引入Ajax优化后出现两个问题:

  • 将Ajax脚本放在{% for post in posts %}循环外时,必须手动刷新页面才能看到点赞状态变化
  • 将脚本放在循环内时,点赞计数器出现混乱,页面所有帖子的点赞/取消状态都会被反转
相关代码

posts/urls.py

urlpatterns = [
    path('create/', PostCreateView.as_view(), name='create_post'),
    path('like/', like_view, name='like_post'),
    path('<int:pk>/update/', PostUpdateView.as_view(), name='update_post'),
    path('<int:pk>/delete/', PostDeleteView.as_view(), name='delete_post'),
    path('<int:pk>/', PostDetailView.as_view(), name='detail_post'),
]

posts/views.py

def like_view(request, *args, **kwargs):
    post = get_object_or_404(Post, id=request.POST.get('post_id'))
    if post.likes.filter(id=request.user.id).exists():
        post.likes.remove(request.user)
    else:
        post.likes.add(request.user)

    context = {
        'post': post,
    }
    if is_ajax(request):
        html = render_to_string('like_template.html', context, request=request)
        return JsonResponse({'form': html})

post_inclde.html(被引入用户个人主页和信息流页面)

<script>
$(document).ready(function(){
  $(document).on('click', '#like', function(event){
    event.preventDefault();
    var pk = $(this).attr('value');
    $.ajax({
      type: 'POST',
      url: '{% url \"like_post\" request.user.slug %}',
      data: {
            'post_id': pk,
            'csrfmiddlewaretoken': '{{ csrf_token }}'
            },
            success: function(response){
              $('#like_section_{{ post.id }}').html(response['form'])
            },
            error: function(rs, e){
              console.log(rs.responseText);
            },
          });
      crossDomain: false
     });
})
</script>

{% if posts %}
    {% for post in posts %}
        <div class="card mb-3 border-0 shadow">
            <h5 class="card-title">{{ post.title }}</h5>
            {{ post.body|safe }}
        </div>
        {% include 'like_template.html' %}
    {% endfor %}
{% endif %}

like_template.html

<div id="like_section_{{ post.id }}">
    <div class="row mb-3">
        <div class="col-md-4 d-flex justify-content-start">
            <form action="{% url 'like_post' post.author.slug %}" method="POST">
                {% csrf_token %}
                {% if request.user in post.likes.all %}
                <button type="submit" id="like" name="post_id" value="{{ post.id }}" class="btn btn-primary btn-sm"
                        style="border-radius: 60px;">
                    <img src="{% static 'icons/heartbreak-fill.svg' %}" alt="Unlike Icon" width="16" height="16"> {{ post.total_likes }}
                </button>
                {% else %}
                <button type="submit" id="like" name="post_id" value="{{ post.id }}" class="btn btn-primary btn-sm"
                        style="border-radius: 60px;">
                    <img src="{% static 'icons/heart-fill.svg' %}" alt="Like Icon" width="16" height="16"> {{ post.total_likes }}
                </button>
                {% endif %}
            </form>
            <button type="submit" name="post_id" value="{{ post.id }}" class="btn btn-primary btn-sm ms-2"
                    style="border-radius: 60px;" onclick="hide_comments('comment_section_{{ post.id }}')">
                <img src="{% static 'icons/chat-fill.svg' %}" alt="Post Comments Icon" width="16" height="16"> {{ post.comments.count }}
            </button>
        </div>
    </div>
</div>
问题分析与修复方案

核心问题1:重复ID引发的事件绑定混乱

HTML中ID必须唯一,但所有点赞按钮都用了id="like",循环内生成的多个重复ID会导致:

  • 脚本放循环外时,事件委托能触发,但回调里的{{ post.id }}不在循环上下文,无法渲染出具体帖子ID,找不到目标DOM更新状态
  • 脚本放循环内时,会生成多份重复的事件绑定,点击任意按钮会触发所有绑定事件,导致所有帖子状态反转

核心问题2:URL匹配错误

urls.py中like_post的路径是path('like/', ...),不接收slug参数,但模板中{% url "like_post" request.user.slug %}和form的action="{% url 'like_post' post.author.slug %}"都错误传递了slug,会导致请求失败。


具体修复步骤

  1. 替换唯一ID为类名
    修改like_template.html中的按钮ID为类,避免重复:

    <!-- 已点赞状态按钮 -->
    <button type="submit" class="like-btn" name="post_id" value="{{ post.id }}" class="btn btn-primary btn-sm"
            style="border-radius: 60px;">
        <img src="{% static 'icons/heartbreak-fill.svg' %}" alt="Unlike Icon" width="16" height="16"> {{ post.total_likes }}
    </button>
    <!-- 未点赞状态按钮 -->
    <button type="submit" class="like-btn" name="post_id" value="{{ post.id }}" class="btn btn-primary btn-sm"
            style="border-radius: 60px;">
        <img src="{% static 'icons/heart-fill.svg' %}" alt="Like Icon" width="16" height="16"> {{ post.total_likes }}
    </button>
    
  2. 修正Ajax脚本,动态获取目标区域
    将脚本放在循环外,修改事件绑定为类选择器,通过点击按钮动态定位对应点赞区域:

    <script>
    $(document).ready(function(){
      $(document).on('click', '.like-btn', function(event){
        event.preventDefault();
        var $btn = $(this);
        var pk = $btn.attr('value');
        // 找到当前按钮所属的点赞区域ID
        var sectionId = $btn.closest('[id^="like_section_"]').attr('id');
        
        $.ajax({
          type: 'POST',
          url: '{% url "like_post" %}',
          data: {
                'post_id': pk,
                'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function(response){
                  $('#' + sectionId).html(response['form'])
                },
                error: function(rs, e){
                  console.log(rs.responseText);
                },
              crossDomain: false
            });
       });
    })
    </script>
    
  3. 修正form的action属性
    修改like_template.html中的form路径,去掉多余的slug参数:

    <form action="{% url 'like_post' %}" method="POST">
    

内容的提问来源于stack exchange,提问作者jmur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:22:03