【问题标题】:How to include a form in multiple templates如何在多个模板中包含一个表单
【发布时间】:2013-09-26 23:18:03
【问题描述】:

我有一个 Django 应用程序,有两种类型的用户,我们称它们为 A 和 B。这两种用户类型有不同的导航栏。用户类型 A 在其导航栏中有一个链接,允许他们发送消息。发送消息表单出现在叠加层中,因此表单需要出现在每个模板中,或者通过 AJAX 获取。这些用户可以从网站上的任何/每个页面发送消息,并且每个页面都有自己的视图。如果消息有错误,则应将用户返回到他们所在的同一页面,并且错误应显示在表单叠加层中。如果发送成功,他们应该被重定向到显示他们消息的新页面。

目前,我已将表单放入一个单独的模板中,该模板包含在用户类型 A 的所有页面中。我将表单操作指定为一个名为 send_message 的视图,并告诉该视图呈现到此模板。视图永远不会被调用,所以我想这不是正确的方法。

如何在每个模板中呈现此表单?我是否需要从每个视图调用一个函数,或者使用一个特殊的模板标签(我查看了包含标签,但我认为它们不适合这种情况)?或者我应该使用 AJAX 获取表单吗?我是否将表单视图添加到每个 URL?

编辑:添加一些代码来解释。

举例来说,我在一个页面上的简单视图如下:

@login_required
def index_view(request, place_id):
    categories = Category.objects.all()
    return render(request, 'places/categories.html', {'place_id': place_id, 'categories': categories})

此页面的模板是从带有导航栏的模板扩展而来的,该模板包括发送消息表单,如下所示:

<div id="message-popup">
    <h1>NewMessage</h1>
        <form action="{% url "send_message" %}" method="post">
            {% csrf_token %}
            {% get_message_form as message_form %}
            <table>
                <tr>
                    <td>{{ message_form.recipient.label_tag }}{{ message_form.recipient }}</span></td>
                    <td>{{ message_form.title.label_tag }}{{ message_form.title }}</td>
                </tr>
            <tr>
                <td>{{ message_form.categories.label_tag }}{{ message_form.categories }}</td>
                <td>
                    {{ message_form.content.label_tag }}{{ message_form.content }}
                    <button type="submit" class="button">Send</button>
                    <button class="box-close button">Cancel</button>
                </td>
            </tr>
        </table>
    </form>
</div>

该表单发布到此视图:

def send_message_view(request, place_id):
    place = Place.objects.get(id=place_id)
    if request.POST:   
        user = get_user_class(request.user)
        message_form = forms.SendMessageForm(request.POST, place=place, user=user)
        if message_form.is_valid():
            message_form.save()
            # redirect to page showing the message
    else:
        message_form = forms.SendMessageForm(place=place, user=user)
    return render(request, 'messaging/send_message_form.html', {'message_form': message_form})

我缺少的步骤是如何让表单首先呈现。当我转到类别页面时,表单不存在 - 显然是因为上下文中没有 message_form 变量。那么我可以让我的 send_message_view 和我的 index_view 运行和渲染这两个模板,还是有其他方法我必须将它们链接在一起?

编辑:

好的,现在这是我的模板标签:

@register.inclusion_tag('messaging/send_message_form.html', name='get_message_form', takes_context=True)
def get_message_form(context, place, sender, recipient):
    message_form = SendMessageForm(context['request'].POST or None, place=place, sender=sender, recipient=recipient)
    url = None
    if context['request'].method=='POST' and message_form.is_valid():
        message = message_form.save()
        url = reverse('conversation', kwargs={'place_slug': place.slug, 'message_id': message.id})
        """ This doesn't work
        return HttpResponseRedirect(url) """
    return {'message_form': message_form, 'place_id': place.id, 'message_sent_url': url}

我不能从模板标签做一个 HttpResponseRedirect,所以我需要找到一些其他的方法来重定向到成功页面。我可以将重定向 URL 发送到模板,但我不知道从那里可以用它做什么...

【问题讨论】:

    标签: django django-forms django-templates django-views


    【解决方案1】:
    1. 您有两个选择 - 将表单放在 base.html 模板文件中,让每个模板都继承自该文件,或者制作一个单独的模板文件,就像您现在所做的那样,然后将其 {%include%} 放在任何位置你想要它。
    2. 不确定“从每个视图调用一个函数”是什么意思。视图是一个函数或一个类。
    3. AJAX 功能是一个单独的问题 - 首先根据需要获取表单呈现,所有继承都使用同步调用。然后根据需要实现 AJAX。
    4. 您的表单将转到您在该表单中指定的 url,因此只有该视图函数需要表单逻辑。 更新:

    看起来您想要一个自定义包含标签来呈现您的表单。

    @register.inclusion_tag('<your-form-render-template.html>')
        def get_message_form(place_id, user):
            return {'message_form': SendMessageForm(Place(id=place_id), user)}
    

    然后在你的 index.html 模板中:

    {%load my-template-tags%}
    
    <div id="message-popup">
        <h1>NewMessage</h1>
        {%get_message_form place_id request.user%}
    </div>
    

    您使用标签注册的模板文件然后呈现表单:

        <form action="{% url "send_message" %}" method="post">
            {% csrf_token %}
            <table>
                <tr>
                    <td>{{ message_form.recipient.label_tag }}{{ message_form.recipient }}</span></td>
                    <td>{{ message_form.title.label_tag }}{{ message_form.title }}</td>
                </tr>
            <tr>
                <td>{{ message_form.categories.label_tag }}{{ message_form.categories }}</td>
                <td>
                    {{ message_form.content.label_tag }}{{ message_form.content }}
                    <button type="submit" class="button">Send</button>
                    <button class="box-close button">Cancel</button>
                </td>
            </tr>
        </table>
    </form>
    

    现在,只要您想放置表单,只需加载您的标签并将正确的参数传递给模板标签。轰隆隆!

    添加标签有特定要求,例如应用程序结构等。阅读它here

    更新:

    好的,这很酷。您可以将上下文变量添加到包含标签中。现在你的标签看起来像这样:

    @register.inclusion_tag('<your-form-render-template.html>', takes_context=True)
        def get_message_form(context):
            message_form = forms.SendMessageForm(context['request'].POST or None, place=context['place_id'], user=context['request.user'])
            if context['request'].method=='POST' and message_form.is_valid():
                #redirect to thanks
    
            return {'message_form': SendMessageForm(Place(id=place_id), user)}
    

    现在,您的包含标签与旧表单视图功能的用途相同。您的表单现在可以只有一个动作“。”,它不需要在表单上执行任何逻辑,标签可以做到。

    请注意,您的模板中有错误 - 您需要使用 {{message_form.errors}} 明确显示表单错误。

    感谢这个极好的问题 - 以前从未这样做过,而且它非常强大。

    最终更新!!!

    好的,对你的最后一点帮助,那么你就靠你自己了:-) 它可能不适用于重定向,因为这个包含标签只是返回要显示的更新的上下文变量。换句话说,一旦你到达这里,你就无法重定向。因此,由于我们只想向用户显示一条消息说“你成功了”,所以让我们使用 messaging 框架。

    @register.inclusion_tag('messaging/send_message_form.html', name='get_message_form', takes_context=True)
    def get_message_form(context, place, sender, recipient):
        from django.contrib import messages
    
        message_form = SendMessageForm(context['request'].POST or None, place=place, sender=sender, recipient=recipient)
        url = None
        if context['request'].method=='POST' and message_form.is_valid():
            message = message_form.save()
            messages.add_message(context['request'], messages.INFO, "Dante Rules!")
    
        return {'message_form': message_form, 'place_id': place.id, 'message_sent_url': url}
    

    现在该消息将在您的主模板中可用,因此您可以在使用包含标记呈现表单后检查它:

        {% for message in messages %}
            <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
        {% endfor %}
    

    一旦被渲染,它就会从上下文中消失,这对我们的例子来说是完美的。因此,您可以在 JQuery UIDialog、页面的另一部分或其他任何地方显示它。无论如何,它看起来比重定向恕我直言更好,因为您停留在发送消息的同一页面上。

    【讨论】:

    • 感谢您的回答。如何将表单获取到模板?我最初像往常一样编写了一个表单和一个视图,但表单并没有以这种方式呈现。如何将表单传递给模板?
    • 您能否发布当前不起作用的视图,我们可以看看。
    • 感谢您更新您的答案。表单现在可以正确呈现,但我不确定如何处理错误。我想我需要我的 send_message_view 将更新后的表单呈现为与用户当前正在查看的模板相同的模板(在我的示例中为 places/categories.html)。有没有办法可以将当前模板名称作为 URL 参数传递给视图?
    • 好的,这有效,但我无法从模板标签重定向 - 我已将更新后的代码添加到问题中。再次感谢您的帮助,这是一个棘手的问题,但如果我们能够让它工作,那就太棒了。
    • 重定向确实有效 - 我已经在我的开发环境中成功完成了它。当您设置takes_context=True 时,标签函​​数仅将'context' 作为arg。你已经传递了额外的参数。将它们设置在上下文变量中以正确访问它们。祝你好运!
    猜你喜欢
    • 2013-10-02
    • 1970-01-01
    • 2012-08-15
    • 2016-04-07
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    • 1970-01-01
    • 2016-04-10
    相关资源
    最近更新 更多