2013-03-19 47 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 %}別名表單來避免重新讀取/重建表單。

相關問題