正确的方法来处理在Django的一个页面上的多个表单

我有一个期望两种forms的模板页面。 如果我只使用一种forms,就像在这个典型的例子中那样:

if request.method == 'POST': form = AuthorForm(request.POST,) if form.is_valid(): form.save() # do something. else: form = AuthorForm() 

但是,如果我想用多种forms工作,我如何让视图知道我只提交了其中一个表单而不是其他表单(即它仍然是request.POST,但我只想处理提交的表单事情)吗?


这是基于答案的解决scheme ,其中, expectedphrasebannedphrase是不同forms的提交button的名称, expectedphraseformbannedphraseform是forms。

 if request.method == 'POST': if 'bannedphrase' in request.POST: bannedphraseform = BannedPhraseForm(request.POST, prefix='banned') if bannedphraseform.is_valid(): bannedphraseform.save() expectedphraseform = ExpectedPhraseForm(prefix='expected') elif 'expectedphrase' in request.POST: expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected') if expectedphraseform.is_valid(): expectedphraseform.save() bannedphraseform = BannedPhraseForm(prefix='banned') else: bannedphraseform = BannedPhraseForm(prefix='banned') expectedphraseform = ExpectedPhraseForm(prefix='expected') 

你有几个select:

  1. 在这两个表单的操作中join不同的url。 那么你将有两种不同的视图function来处理这两种不同的forms。

  2. 从POST数据中读取提交button的值。 你可以告诉哪个提交button被点击了: 我怎样才能build立多个提交buttonDjango的forms?

未来的参考方法是这样的。 bannedphraseform是第一种forms,而预期的词法是第二种。 如果第一个被击中,第二个被跳过(在这种情况下这是一个合理的假设):

 if request.method == 'POST': bannedphraseform = BannedPhraseForm(request.POST, prefix='banned') if bannedphraseform.is_valid(): bannedphraseform.save() else: bannedphraseform = BannedPhraseForm(prefix='banned') if request.method == 'POST' and not bannedphraseform.is_valid(): expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected') bannedphraseform = BannedPhraseForm(prefix='banned') if expectedphraseform.is_valid(): expectedphraseform.save() else: expectedphraseform = ExpectedPhraseForm(prefix='expected') 

Django的基于类的视图提供了一个通用的FormView,但是对于所有的意图和目的,它只能处理一种forms。

使用Django的通用视图来处理具有相同目标动作url的多个表单的一种方法是扩展“TemplateView”,如下所示; 我经常使用这种方法,所以我已经将它制作成Eclipse IDE模板。

 class NegotiationGroupMultifacetedView(TemplateView): ### TemplateResponseMixin template_name = 'offers/offer_detail.html' ### ContextMixin def get_context_data(self, **kwargs): """ Adds extra content to our template """ context = super(NegotiationGroupDetailView, self).get_context_data(**kwargs) ... context['negotiation_bid_form'] = NegotiationBidForm( prefix='NegotiationBidForm', ... # Multiple 'submit' button paths should be handled in form's .save()/clean() data = self.request.POST if bool(set(['NegotiationBidForm-submit-counter-bid', 'NegotiationBidForm-submit-approve-bid', 'NegotiationBidForm-submit-decline-further-bids']).intersection( self.request.POST)) else None, ) context['offer_attachment_form'] = NegotiationAttachmentForm( prefix='NegotiationAttachment', ... data = self.request.POST if 'NegotiationAttachment-submit' in self.request.POST else None, files = self.request.FILES if 'NegotiationAttachment-submit' in self.request.POST else None ) context['offer_contact_form'] = NegotiationContactForm() return context ### NegotiationGroupDetailView def post(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) if context['negotiation_bid_form'].is_valid(): instance = context['negotiation_bid_form'].save() messages.success(request, 'Your offer bid #{0} has been submitted.'.format(instance.pk)) elif context['offer_attachment_form'].is_valid(): instance = context['offer_attachment_form'].save() messages.success(request, 'Your offer attachment #{0} has been submitted.'.format(instance.pk)) # advise of any errors else messages.error('Error(s) encountered during form processing, please review below and re-submit') return self.render_to_response(context) 

html模板的作用如下:

 ... <form id='offer_negotiation_form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8"> {% csrf_token %} {{ negotiation_bid_form.as_p }} ... <input type="submit" name="{{ negotiation_bid_form.prefix }}-submit-counter-bid" title="Submit a counter bid" value="Counter Bid" /> </form> ... <form id='offer-attachment-form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8"> {% csrf_token %} {{ offer_attachment_form.as_p }} <input name="{{ offer_attachment_form.prefix }}-submit" type="submit" value="Submit" /> </form> ... 

我需要在同一页面上独立validation的多个表单。 我遗漏的关键概念是1)使用提交button名称的表单前缀和2)无界forms不会触发validation。 如果它帮助别人,这里是我使用TemplateView的两种formsAForm和BForm的简单示例,基于@ adam-nelson和@ daniel-sokolowski的回答以及@zeraien( https://stackoverflow.com/a/17303480 / 2680349 ):

 # views.py def _get_form(request, formcls, prefix): data = request.POST if prefix in request.POST else None return formcls(data, prefix=prefix) class MyView(TemplateView): template_name = 'mytemplate.html' def get(self, request, *args, **kwargs): return self.render_to_response({'aform': AForm(prefix='aform_pre'), 'bform': BForm(prefix='bform_pre')}) def post(self, request, *args, **kwargs): aform = _get_form(request, AForm, 'aform_pre') bform = _get_form(request, BForm, 'bform_pre') if aform.is_bound and aform.is_valid(): # Process aform and render response elif bform.is_bound and bform.is_valid(): # Process bform and render response return self.render_to_response({'aform': aform, 'bform': bform}) # mytemplate.html <form action="" method="post"> {% csrf_token %} {{ aform.as_p }} <input type="submit" name="{{aform.prefix}}" value="Submit" /> {{ bform.as_p }} <input type="submit" name="{{bform.prefix}}" value="Submit" /> </form> 

这有点晚,但这是我find的最好的解决scheme。 您为表单名称及其类别创build一个查找字典,还必须添加一个属性来标识表单,并且在您的视图中,您必须将其作为隐藏字段添加到form.formlabel

 # form holder form_holder = { 'majeur': { 'class': FormClass1, }, 'majsoft': { 'class': FormClass2, }, 'tiers1': { 'class': FormClass3, }, 'tiers2': { 'class': FormClass4, }, 'tiers3': { 'class': FormClass5, }, 'tiers4': { 'class': FormClass6, }, } for key in form_holder.keys(): # If the key is the same as the formlabel, we should use the posted data if request.POST.get('formlabel', None) == key: # Get the form and initate it with the sent data form = form_holder.get(key).get('class')( data=request.POST ) # Validate the form if form.is_valid(): # Correct data entries messages.info(request, _(u"Configuration validée.")) if form.save(): # Save succeeded messages.success( request, _(u"Données enregistrées avec succès.") ) else: # Save failed messages.warning( request, _(u"Un problème est survenu pendant l'enregistrement " u"des données, merci de réessayer plus tard.") ) else: # Form is not valid, show feedback to the user messages.error( request, _(u"Merci de corriger les erreurs suivantes.") ) else: # Just initiate the form without data form = form_holder.get(key).get('class')(key)() # Add the attribute for the name setattr(form, 'formlabel', key) # Append it to the tempalte variable that will hold all the forms forms.append(form) 

我希望这将有助于未来。

这是处理上述的简单方法。

在HTML模板中,我们把邮政

 <form action="/useradd/addnewroute/" method="post" id="login-form">{% csrf_token %} <!-- add details of form here--> <form> <form action="/useradd/addarea/" method="post" id="login-form">{% csrf_token %} <!-- add details of form here--> <form> 

在视图中

  def addnewroute(request): if request.method == "POST": # do something def addarea(request): if request.method == "POST": # do something 

在URL中提供需要的信息

 urlpatterns = patterns('', url(r'^addnewroute/$', views.addnewroute, name='addnewroute'), url(r'^addarea/', include('usermodules.urls')), 

如果你正在使用基于课堂的观点和不同的“行动”attrs我的意思

在这两个表单的操作中join不同的url。 那么你将有两种不同的视图function来处理这两种不同的forms。

您可以使用重载的get_context_data方法轻松处理来自不同forms的错误,例如:

views.py:

 class LoginView(FormView): form_class = AuthFormEdited success_url = '/' template_name = 'main/index.html' def dispatch(self, request, *args, **kwargs): return super(LoginView, self).dispatch(request, *args, **kwargs) .... def get_context_data(self, **kwargs): context = super(LoginView, self).get_context_data(**kwargs) context['login_view_in_action'] = True return context class SignInView(FormView): form_class = SignInForm success_url = '/' template_name = 'main/index.html' def dispatch(self, request, *args, **kwargs): return super(SignInView, self).dispatch(request, *args, **kwargs) ..... def get_context_data(self, **kwargs): context = super(SignInView, self).get_context_data(**kwargs) context['login_view_in_action'] = False return context 

模板:

 <div class="login-form"> <form action="/login/" method="post" role="form"> {% csrf_token %} {% if login_view_in_action %} {% for e in form.non_field_errors %} <div class="alert alert-danger alert-dismissable"> {{ e }} <a class="panel-close close" data-dismiss="alert">×</a> </div> {% endfor %} {% endif %} ..... </form> </div> <div class="signin-form"> <form action="/registration/" method="post" role="form"> {% csrf_token %} {% if not login_view_in_action %} {% for e in form.non_field_errors %} <div class="alert alert-danger alert-dismissable"> {{ e }} <a class="panel-close close" data-dismiss="alert">×</a> </div> {% endfor %} {% endif %} .... </form> </div> 

想要分享我的解决scheme,Django表单没有被使用。 我在单个页面上有多个表单元素,我想用单个视图来pipe理所有表单中的所有POST请求。

我所做的是我已经引入了一个不可见的input标签,以便我可以传递一个参数到视图来检查已经提交的表单。

 <form method="post" id="formOne"> {% csrf_token %} <input type="hidden" name="form_type" value="formOne"> ..... </form> ..... <form method="post" id="formTwo"> {% csrf_token %} <input type="hidden" name="form_type" value="formTwo"> .... </form> 

views.py

 def handlemultipleforms(request, template="handle/multiple_forms.html"): """ Handle Multiple <form></form> elements """ if request.method == 'POST': if request.POST.get("form_type") == 'formOne': #Handle Elements from first Form elif request.POST.get("form_type") == 'formTwo': #Handle Elements from second Form