如何在多个模板中包含表单

时间:2013-09-21 18:11:15

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

我有一个有两种类型用户的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,因此我需要找到一些重定向到成功页面的方法。我可以将重定向网址发送到模板,但我不知道从那里可以用它做什么......

1 个答案:

答案 0 :(得分:2)

  1. 您有两种选择 - 将表单放在您的base.html模板文件中,并使每个模板继承该模板,或者创建一个单独的模板文件,就像您目前所做的那样,{%include%}它在哪里你想要它。
  2. 不确定你的意思是'从每个视图调用一个函数'。视图是一个函数或类。
  3. AJAX功能是一个单独的问题 - 首先获得您想要的表单呈现,所有继承都使用同步调用。然后根据需要实现AJAX。
  4. 您的表单将转到您在该表单中指定的网址,因此只有该视图功能才需要表单逻辑。 UPDATE:
  5. 您希望自定义包含标记呈现您的表单。

    @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>
    

    现在,只要您想放置表单,只需加载标记并将正确的args传递给模板标记即可。吊杆!

    添加标记有特定要求,例如应用结构等。请阅读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,页面的另一部分或其他任何部分中显示它。它看起来比重新直接的恕我直言更好,因为你留在你发送消息的同一页面。