以绑定的Django形式动态获取模板名称

时间:2014-07-28 05:36:22

标签: django django-forms django-class-based-views

我正在尝试在任意页面上呈现Django联系表单。我正在使用请求上下文处理器和模板包含。这允许我在任何我想要的地方显示表单。然后我有一个特殊的URL接受POST请求(在GET上,我只是重定向它们)。如果表单有效,我会发送电子邮件,然后重定向到成功页面。在表单无效的情况下,我知道传递带有错误的表单,但是......我不知道要指定哪个模板,因为表单是一个包含,父模板可以在任何地方。

在Django视图中获取内容的唯一方法是来自请求。我可以通过更多的工作获得路径,可能是POST来自的原始视图,但这并不能让我成为模板。

# urls.py
url(r'^services/$', 'website.views.services', name='services'),
url(r'^services/contact/$', 'website.views.services_contact', name='services_contact'),
url(r'^services/contact/done/$', 'website.views.services_contact_done', name='services_contact_done')


# views.py

class ServicesView(TemplateView):
    template_name = 'services/services.html'

services = ServicesView.as_view()

class ServicesContactView(View):

    def get(self, request, *args, **kwargs):
        return redirect('services')

    def post(self, request, *args, **kwargs):
        form = ContactForm(request.POST)
        if form.is_valid():
            form.send_email()
            return redirect('services_contact_done')
        else:
            return render(request, ????, {'contact_form': form})

services_contact = ServicesContactView.as_view()


# contact.html

<h2>Contact me</h2>
<p>Enter your email to receive your questionnaire</p>

<form action="{% url 'services_contact' %}" method="post">

    {% csrf_token %}

    {% if contact_form.non_field_errors %}
    {{ contact_form.non_field_errors }}
    {% endif %}

    {{ contact_form.as_p }}

    <button type="submit" name="submit">Send questionnaire</button>

</form>

# home.html

{% extends "base.html" %}
{% block content %}
<h1>{{ site.name }}</h1>
{% include "services/contact.html" %}
{% endblock %}

典型的Django表单视图在表单上是无声的,因为它的场景大部分类似于未绑定的表单,所以最后只是渲染。由于模板包含,我的情况不同。

2 个答案:

答案 0 :(得分:1)

您可以在每次渲染模板时设置会话变量,并在需要时使用它:

request.session['template']="nameOfTemplate"

return render(request, request.session.get('template', 'default.html'), {'contact_form': form})

我知道每次渲染模板时都需要编写一行代码,但这是我能想到的最佳解决方案。

答案 1 :(得分:0)

如果有人需要这个答案,我自己想出来了。这是可能的,但需要采用不同的方法。首先,请求上下文处理器不适合这种情况。他们相当愚蠢,因为他们只是得到一些东西,并坚持在上下文中。他们唯一的优势是他们的全球性。

我的上下文处理器:

def contact_form(request):
    """
    Gets the contact form and adds it to the request context.
    You almost certainly don't want to do this.
    """
    form = ContactForm()

    return {'contact_form': form}

表单的本质是它们在被Django的验证机制处理后表现不同,具体来说ContactForm()unbound form并且永远是FormMixin。您不希望这样做(除非您想要一个只显示但不起作用的表单)。应编辑TEMPLATE_CONTEXT_PROCESSORS以删除此处理器。

现在,显示表单的负担又回到了视图上,这也意味着任何视图都必须能够处理POST请求。这意味着需要编辑想要联系表单的每个视图,但我们可以使用基于类的视图和mixin的强大功能来处理大部分重复。

ServicesViewTemplateView几乎相同,除了使用可处理表单的mixin。这样,模板名称始终保持不变(我的原始问题),但具有额外的形式功能。

class ServicesView(ContactMixin, TemplateView):
    template_name = 'services/services.html'

services = ServicesView.as_view()

ContactMixin使用ProcessFormView来创建和显示表单,使用different kinds of requests来处理表单的GET和POST请求。并且由于表单的性质随an example in the docs(未提交,已提交和无效,已提交且有效)而变化,因此需要使用正确的表单类实例更新get_context_data。最后,我们可能希望为表单添加前缀(命名空间),因为它可以在任何地方使用,并且当另一个可能的表单可以POST到同一视图时,我们希望避免冲突。因此,mixin是:

class ContactMixin(FormMixin, ProcessFormView):
    form_class = ContactForm
    success_url = reverse_lazy('contact_done')

    def get_form_kwargs(self):
        kwargs = super(ContactMixin, self).get_form_kwargs()
        kwargs['prefix'] = 'contact'
        return kwargs

    def get_context_data(self, **kwargs):
        context = super(ContactMixin, self).get_context_data(**kwargs)
        form_class = self.get_form_class()
        context['contact_form'] = self.get_form(form_class)
        return context

    def form_valid(self, form):
        form.send_email()
        return super(ContactMixin, self).form_valid(form)

self.get_form_class()的微妙之处几乎失去了我,如果它不是StackOverflow answer(不做什么,嘿)和另一个{{3}},我通常只会说self.form_class,忽略了表格的处理。

现在,我只需将ContactMixin添加到任意视图,将{% include "includes/contact.html" %}添加到任何模板。