2013-03-19 50 views
12

如果我有两种形式:Django:基于类的视图可以一次接受两种形式吗?

class ContactForm(forms.Form): 
    name = forms.CharField() 
    message = forms.CharField(widget=forms.Textarea) 

class SocialForm(forms.Form): 
    name = forms.CharField() 
    message = forms.CharField(widget=forms.Textarea) 

,并希望使用基于类视图,并将其发送表单模板,是甚至可能吗?

class TestView(FormView): 
    template_name = 'contact.html' 
    form_class = ContactForm 

看来FormView一次只能接受一种形式。 在基于功能的视图中,虽然我可以轻松地将两个表单发送到我的模板,并在request.POST内部检索两个内容。

variables = {'contact_form':contact_form, 'social_form':social_form } 
return render(request, 'discussion.html', variables) 

这是使用基于类的视图(通用视图)的限制吗?

很多谢谢

+0

你看过FormSets吗? https://docs.djangoproject.com/en/dev/topics/forms/formsets/ 编辑:一些洞察力可能在这里:http://stackoverflow.com/questions/6276398/multiple-form-classes-in- django-generic-class-views – 2013-03-19 11:06:24

+0

除非我有misunderstod表单集,每个表单集都是同一个表单的集合。我的表格是不同的。因此我不认为我可以使用formset。纠正我,如果我错了 – Houman 2013-03-19 11:08:10

回答

1

这不是基于类的视图的限制。泛型FormView并不是用来接受两种形式(好吧,它是通用的)。你可以继承它或者编写你自己的基于类的视图来接受两种形式。

+0

子类化听起来很有趣。你碰巧知道如何做到这一点?我觉得这个新方法很混乱。问题是如果它值得付出努力。为什么不在这种情况下坚持基于功能的视图?这难道不简单吗? – Houman 2013-03-19 12:02:22

+0

这取决于你将如何处理它们。你有两个单独的成功网址吗?两种形式都是GET/POST,他们是否共享相同的动作attr?理解通用视图如何工作的最直接的方法是查看Django代码以查看需要更改的内容 – Marat 2013-03-19 12:10:09

15

默认情况下,基于类的视图仅支持每个视图的单个窗体。但还有其他方法可以完成你所需要的。但是,再次,这不能同时处理这两种形式。这也适用于大多数基于类的观点以及常规形式。

views.py

class MyClassView(UpdateView): 

    template_name = 'page.html' 
    form_class = myform1 
    second_form_class = myform2 
    success_url = '/' 

    def get_context_data(self, **kwargs): 
     context = super(MyClassView, self).get_context_data(**kwargs) 
     if 'form' not in context: 
      context['form'] = self.form_class(request=self.request) 
     if 'form2' not in context: 
      context['form2'] = self.second_form_class(request=self.request) 
     return context 

    def get_object(self): 
     return get_object_or_404(Model, pk=self.request.session['value_here']) 

    def form_invalid(self, **kwargs): 
     return self.render_to_response(self.get_context_data(**kwargs)) 

    def post(self, request, *args, **kwargs): 
     self.object = self.get_object() 
     if 'form' in request.POST: 
      form_class = self.get_form_class() 
      form_name = 'form' 
     else: 
      form_class = self.second_form_class 
      form_name = 'form2' 

     form = self.get_form(form_class) 

     if form.is_valid(): 
      return self.form_valid(form) 
     else: 
      return self.form_invalid(**{form_name: form}) 

模板

<form method="post"> 
    {% csrf_token %} 
    ......... 
    <input type="submit" name="form" value="Submit" /> 
</form> 

<form method="post"> 
    {% csrf_token %} 
    ......... 
    <input type="submit" name="form2" value="Submit" /> 
</form> 
+0

这也解决了同样的问题... http://chriskief.com/2012/12/30/django-class -based-views-with-multiple-forms/ – james 2014-05-29 21:15:25

1

这是一个例子,当 - 至少目前 - 这是更好地恢复到传统的基于功能的看法。基于类的视图不是银色的子弹,最好是使用每种类型的视图来获得最佳的能力。

23

这是一个可扩展的解决方案。我的出发点是这样的主旨,

https://gist.github.com/michelts/1029336

我已经得到增强的解决方案,使多个表单可以显示,但所有或个人可以提交

https://gist.github.com/jamesbrobb/748c47f46b9bd224b07f

这有一个例子使用

class SignupLoginView(MultiFormsView): 
    template_name = 'public/my_login_signup_template.html' 
    form_classes = {'login': LoginForm, 
        'signup': SignupForm} 
    success_url = 'my/success/url' 

    def get_login_initial(self): 
     return {'email':'[email protected]'} 

    def get_signup_initial(self): 
     return {'email':'[email protected]'} 

    def get_context_data(self, **kwargs): 
     context = super(SignupLoginView, self).get_context_data(**kwargs) 
     context.update({"some_context_value": 'blah blah blah', 
         "some_other_context_value": 'blah'}) 
     return context 

    def login_form_valid(self, form): 
     return form.login(self.request, redirect_url=self.get_success_url()) 

    def signup_form_valid(self, form): 
     user = form.save(self.request) 
     return form.signup(self.request, user, self.get_success_url()) 

和模板看起来像这样

<form class="login" method="POST" action="{% url 'my_view' %}"> 
    {% csrf_token %} 
    {{ forms.login.as_p }} 

    <button name='action' value='login' type="submit">Sign in</button> 
</form> 

<form class="signup" method="POST" action="{% url 'my_view' %}"> 
    {% csrf_token %} 
    {{ forms.signup.as_p }} 

    <button name='action' value='signup' type="submit">Sign up</button> 
</form> 

模板上要注意的一件重要事情是提交按钮。他们必须将他们的'name'属性设置为'action',他们的'value'属性必须与'form_classes'字典中给出的表单名称匹配。这用于确定哪个单独的表单已被提交。

+0

谢谢James!这很光滑!一个问题,但。您的_form_valid返回表单的示例。

(),但这看起来不正确。那些只是返回forms_valid()? – David 2015-03-11 18:23:28

+0

@David这些方法被'forms_valid()调用' – james 2015-03-12 09:59:58

+0

@james我正在尝试使用你的解决方案。我明白'def get_login_initial'和'def get_signup_initial'只是将email字段设置为默认值(为用户保存一些键入内容)。如果我不想预先填充表单上的任何数据,我不必编写这两种方法?例如,我有一个参考表格,如果求职者的参考文献是有效的,该表格将会更新。所以,我应该:'def get_reference1_initial:pass','def get_reference2_initial:pass','def get_reference3_initial:pass'?谢谢。 – 2018-01-03 23:35:42

0

我已经使用基于templateview后面的通用视图:

def merge_dicts(x, y): 
    """ 
    Given two dicts, merge them into a new dict as a shallow copy. 
    """ 
    z = x.copy() 
    z.update(y) 
    return z 


class MultipleFormView(TemplateView): 
    """ 
    View mixin that handles multiple forms/formsets. 
    After the successful data is inserted ``self.process_forms`` is called. 
    """ 
    form_classes = {} 

    def get_context_data(self, **kwargs): 
     context = super(MultipleFormView, self).get_context_data(**kwargs) 
     forms_initialized = {name: form(prefix=name) 
          for name, form in self.form_classes.items()} 

     return merge_dicts(context, forms_initialized) 

    def post(self, request): 
     forms_initialized = { 
      name: form(prefix=name, data=request.POST) 
      for name, form in self.form_classes.items()} 

     valid = all([form_class.is_valid() 
        for form_class in forms_initialized.values()]) 
     if valid: 
      return self.process_forms(forms_initialized) 
     else: 
      context = merge_dicts(self.get_context_data(), forms_initialized) 
      return self.render_to_response(context) 

    def process_forms(self, form_instances): 
     raise NotImplemented 

这样做的优点是可重复使用的,所有的验证的形式本身来完成。

它然后被用于从动地:

class AddSource(MultipleFormView): 
    """ 
    Custom view for processing source form and seed formset 
    """ 
    template_name = 'add_source.html' 
    form_classes = { 
     'source_form': forms.SourceForm, 
     'seed_formset': forms.SeedFormset, 
    } 

    def process_forms(self, form_instances): 
     pass # saving forms etc 
7

及其可能的是一个基于类的视图接受一次两种形式。

view.py

class TestView(FormView): 
    template_name = 'contact.html' 
    def get(self, request, *args, **kwargs): 
     contact_form = ContactForm() 
     contact_form.prefix = 'contact_form' 
     social_form = SocialForm() 
     social_form.prefix = 'social_form' 
     return self.render_to_response(self.get_context_data('contact_form':contact_form, 'social_form':social_form)) 

    def post(self, request, *args, **kwargs): 
     contact_form = ContactForm(self.request.POST, prefix='contact_form') 
     social_form = SocialForm(self.request.POST, prefix='social_form ') 

     if contact_form.is_valid() and social_form.is_valid(): 
      ### do something 
      return HttpResponseRedirect(>>> redirect url <<<) 
     else: 
      return self.form_invalid(contact_form,social_form , **kwargs) 


    def form_invalid(self, contact_form, social_form, **kwargs): 
     contact_form.prefix='contact_form' 
     social_form.prefix='social_form' 
       return self.render_to_response(self.get_context_data('contact_form':contact_form, 'social_form':social_form)) 

forms.py

from django import forms 
from models import Social, Contact 
from crispy_forms.helper import FormHelper 
from crispy_forms.layout import Submit, Button, Layout, Field, Div 
from crispy_forms.bootstrap import (FormActions) 

class ContactForm(forms.ModelForm): 
    class Meta: 
     model = Contact 
    helper = FormHelper() 
    helper.form_tag = False 

class SocialForm(forms.Form): 
    class Meta: 
     model = Social 
    helper = FormHelper() 
    helper.form_tag = False 

HTML

以一个外形式类和设定动作作为TestView地址

{% load crispy_forms_tags %} 
<form action="/testview/" method="post"> 
    <!----- render your forms here --> 
    {% crispy contact_form %} 
    {% crispy social_form%} 
    <input type='submit' value="Save" /> 
</form> 

好运

+0

这个解决方案可以工作,但唯一的问题是,如果我使用的是contact_form = ContactForm(self.request.POST,prefix ='contact_form') social_form = SocialForm(self.request.POST, prefix ='social_form') '但它工作如果从这两个表格中删除前缀。我不明白这种行为。 – javed 2017-03-12 08:18:40

+0

用于最初生成表格的前缀。 – 2017-03-14 05:57:05

0

使用django-superform

这是一个线程组成形式为单一对象外部来电,如Django的类为本次漂亮整洁的方式。

from django_superform import FormField, SuperForm 

class MyClassForm(SuperForm): 
    form1 = FormField(FormClass1) 
    form2 = FormField(FormClass2) 

在视图中,可以使用form_class = MyClassForm

在形式__init__()方法,您可以使用访问形式:self.forms['form1']

还为模型,形成了SuperModelFormModelFormField

在模板中,您可以使用以下格式访问表单域:{{ form.form1.field }}。我会建议使用{% with form1=form.form1 %}别名表单来避免重新读取/重建表单。

相关问题