Django包含for循环中的模板标记仅捕获第一次迭代

时间:2016-07-07 16:12:01

标签: python django loops django-templates closures

我在我的网站上的一些页面上有一个评论部分,我使用{% for ... %}循环构建(另一个嵌套循环用于评论回复。该部分被黑客攻击,我仍在学习Web开发和Django,所以请原谅任何令人沮丧的邋or或怪异。我现在不关心效率,只关注功效,现在它不能正常工作。

对于每个评论,我都有一个Bootstrap下拉按钮,它会显示选项EditDeleteEdit会打开一个模态来编辑评论。模态使用{% include %}标记进行渲染。下面我已经将部分代码未经修改地包括在内,而不是试图简化我的示例并冒险留下一些关键的东西:

<div class="panel panel-default">
    {% for comment in spot.ordered_comments %}
    <div class="panel-heading row">
        <div class="col-sm-10">
            <strong>{{ comment.poster.username }}</strong>
            <em style="margin-left: 2em">{{ comment.created|date:'M d \'y \a\t H:i' }}</em>
        </div>
        <div class="btn-group col-sm-2" role="group">

            {% if comment.poster == user %}
            <form id="delete-comment-form" class="form"
                  method="post" action="{% url 'delete_comment' spot.id comment.id %}">
                {% csrf_token %}
            </form>

            {% include 'topspots/editmodal.html' with edit_type='comment' %}

            <div class="btn-group">
                <button type="button" class="btn btn-default dropdown-toggle"
                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                    <i class="fa fa-edit"></i> <span class="caret"></span>
                </button>
                <ul class="dropdown-menu">
                    <li><a href="#" data-toggle="modal" data-target="#editModal">Edit</a></li>
                    <li role="separator" class="divider"></li>
                    <li>
                        <a href="javascript:;" onclick="$('#delete-comment-form').submit();">Delete</a>
                    </li>
                </ul>
            </div>

            {% endif %}

            {% if user.is_authenticated %}
            {% include 'topspots/replymodal.html' %}
            <button type="button" class="btn btn-default" data-toggle="modal"
                    data-target="#replyModal">
                Reply
            </button>
            {% endif %}
        </div>
    </div>

    <div class="panel-body">
        <div class ="row">
            <div class="col-sm-8">
                {{ comment.comment_text }}
            </div>
        </div>
        <br/>

        <!-- Comment replies -->
        {% if comment.commentreply_set %}
        {% for reply in comment.commentreply_set.all %}
        <div class="row" style="padding-left: 1em">
            <div class="col-sm-8 well">
                <p>{{ reply.reply_text }}</p>
                <div class="row">
                    <div class="col-sm-4">
                        <p>
                            <strong>{{ reply.poster.username }}</strong>
                            <em style="margin-left: 2em">{{ comment.created|date:'M d \'y \a\t H:i' }}</em>
                        </p>
                    </div>
                    {% if reply.poster == user %}
                    {% include 'topspots/editmodal.html' with edit_type='reply' %}
                    <form id="delete-reply-form" class="form"
                          method="post" action="{% url 'delete_reply' spot.id reply.id %}">
                        {% csrf_token %}
                    </form>
                    <div class="col-sm-2">
                        <div class="btn-group">
                            <button type="button" class="btn btn-default dropdown-toggle"
                                    data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                <i class="fa fa-edit"></i> <span class="caret"></span>
                            </button>
                            <ul class="dropdown-menu">
                                <li><a href="#" data-toggle="modal"
                                       data-target="#editModal">Edit</a></li>
                                <li role="separator" class="divider"></li>
                                <li>
                                    <a href="javascript:;"
                                       onclick="$('#delete-reply-form').submit();">Delete</a>
                                </li>
                            </ul>
                        </div>
                    </div>
                    {% endif %}
                </div>
            </div>
        </div>
        {% endfor %}
        {% endif %}

    </div>
    {% endfor %}
</div>

以下是编辑模式:

<!-- editmodal.html -->
{% load static %}

<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span></button>
                <h2 class="modal-title" id="editModalLabel">
                    Edit {{ edit_type }}:
                </h2>
            </div>
            <form action="{% url 'edit_comment' spot.id comment.id %}" method="post">
                <div class="modal-body">
                    <input class="form-control" name="text" value="{{ comment.comment_text }}" autofocus>
                    <input type="hidden" name="edit_type" value="{{ edit_type }}">
                    {% csrf_token %}
                </div>
                <div class="modal-footer">
                        <button type="submit" class="btn btn-default">Finish editing</button>
                </div>
            </form>
        </div>
    </div>
</div>
<script>

    $('.modal').on('shown.bs.modal', function() {
        $(this).find('[autofocus]').focus();
    });

</script>

和回复模式:

<!-- replymodal.html -->
{% load static %}

<div class="modal fade" id="replyModal" tabindex="-1" role="dialog" aria-labelledby="replyModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span></button>
                <h2 class="modal-title" id="replyModaLabel">
                    Reply to <strong>{{ comment.poster.username }}'s</strong> comment
                </h2>
            </div>
            <div class="modal-body">
                <form action="{% url 'reply_comment' spot.id comment.id %}" method="post">
                    <input class="form-control" name="reply_text" placeholder="Write a reply..." autofocus>
                    {% csrf_token %}
                </form>
            </div>
        </div>
    </div>
</div>
<script>

    $('.modal').on('shown.bs.modal', function() {
        $(this).find('[autofocus]').focus();
    });

</script>

我遇到的问题是我的回复和编辑模式(例如{% include 'topspots/editmodal.html' with edit_type='reply' %}{% include 'topspots/replymodal.html' %}似乎只在我的for循环的第一次迭代的上下文中呈现一次。所以即使所有当我点击回复,编辑或删除时,无论我点击哪个按钮(即,我是否单击第一个评论的按钮,或第五个评论等),问题都会在页面上正确呈现,我只能回复,编辑或删除第一条评论。我有一种感觉,这与闭包和范围有关,我不太了解(过去我遇到了麻烦,使用lambda出现意外结果在Python循环中由于thisthis),但我不确定。

我使用以下视图进行了测试:

def test(request):
    spots = Spot.objects.all()
    return render(request, 'test.html', {'spots': spots})

和模板:

<!-- test.html -->
<h1>Hello world</h1>
{% for spot in spots %}
    {% include 'testinclude.html' %}
{% endfor %}

<!-- testinclude.html -->
<h3>{{ spot.name }}</h3>

它打印出一个独特的名字列表,为什么与模态的区别?

2 个答案:

答案 0 :(得分:3)

As emulbreh postulates, a modal is in fact rendered for every comment. However, all of the modals have the same ID, so regardless of which comment’s edit button was clicked, the first modal gets triggered every time. IDs are supposed to be unique across an HTML document.

How can you fix this? You can make the IDs of the modals unique to each comment. You can get a unique identifier by writing id="editModal-{{ comment.id }}" or just id="editModal-{{ forloop.counter }} (documentation here).

But then your editModal.html template is coupled very tightly with your ‘master’ template. A better solution would be to use classes instead of IDs and put the identification where it belongs: the container of each comment. You can try:

  1. adding an ID to each comment’s container:

    <div class="panel panel-default">
    {% for comment in spot.ordered_comments %}
      <div class="panel-heading row" id="comment-{{ comment.id }}">
        ...
    
  2. using classes instead of IDs in your modal templates as so:

    <!-- editmodal.html -->
    {% load static %}
    
    <div class="modal fade editModal" tabindex="-1" ...>
      ...
    
  3. changing data-target in your buttons from:

    <li><a href="#" data-toggle="modal" data-target="#editModal">Edit</a></li>
    

    to:

    <li><a href="#" data-toggle="modal" data-target="#comment-{{ comment.id }} .editModal">Edit</a></li>
    

答案 1 :(得分:2)

看起来您的所有编辑模式都具有相同的ID id="editModal"以及您的回复模式id="replyModal"如果您基于ID显示它们可能您将始终打开第一个DOM元素ID。您可以尝试添加forloop.counter

等唯一标识符

https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#for