8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

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

picobit 2月前

187 0

我有一个模板页面需要两个表单。如果我只使用一个表单,那么一切正常,如以下典型示例所示:if request.method == 'POST': form = AuthorForm(request.POST,) if form.is_va...

我有一个模板页面需要两个表单。如果我只使用一个表单,那么一切就都正常了,如以下典型示例所示:

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

但是,如果我想使用多个表单,我该如何让视图知道我只提交其中一个表单而不是另一个表单(即它仍然是 request.POST 但我只想处理发生提交的表单)?


这是 基于答案的解决方案,其中 expectedphrase bannedphrase 是不同表单的提交按钮的名称,而 expectedphraseform bannedphraseform 是表单。

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')
帖子版权声明 1、本帖标题:在Django中处理一个页面上的多个表单的正确方法
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由picobit在本站《forms》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 您应该添加一些链接(以及其中内容的引用)来支持这一断言。就目前而言,现有的 Django 文档为表单提供了前缀,以避免在呈现到页面时发生名称冲突,而且我没有看到任何内容说我不能呈现多个表单,只要它们在回发时能够充分区分即可。(这可以通过为每个表单提供自己的端点轻松实现。)

  • 我发现了一种非常有趣的方法,可以使用相同的视图从单个页面发送 两个表单 在页面上 只有 两个表单

    我只是使用 try and except 的方法 try ,如果不起作用,则尝试第二个表单。知道它完全正常工作,这很有趣。不要在可扩展的应用程序上使用它,因为它可能会造成麻烦或可能危及应用程序的安全,否则请使用基于类的视图来提交多个表单或为每个表单创建单独的视图。

    def create_profile(request):
        if request.method=='POST':
            try:       
                biograph = Biography(name=name, email=email, full_name=full_name, slug_name=slug_name, short_bio=short_bio)
                biograph.save()
    
            except:
                social = SocialMedia(twitter=twitter, instagram=instagram, facebook=facebook, linkedin=linkedin, github=github)
                social.save()
    
  • if request.method == 'POST':
        expectedphraseform = ExpectedphraseForm(request.POST)
        bannedphraseform = BannedphraseForm(request.POST)
        if expectedphraseform.is_valid():
            expectedphraseform.save()
            return HttpResponse("Success")
        if bannedphraseform.is_valid():
            bannedphraseform.save()
            return HttpResponse("Success")
    else:
        bannedphraseform = BannedphraseForm()
        expectedphraseform = ExpectedphraseForm()
    return render(request, 'some.html',{'bannedphraseform':bannedphraseform, 'expectedphraseform':expectedphraseform})
    

    对我来说,这种方法正如我所想的那样,准确无误。这种方法有一个问题,即它会验证表单的两个错误。但效果非常好。

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

    在 Html 模板中我们放入 Post

    <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')),
    
  • 如果你使用基于类的视图和不同的“动作”属性的方法,我的意思是

    在操作中为两个表单设置不同的 URL。然后,您将拥有两个不同的视图函数来处理这两个不同的表单。

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

    视图.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>
    
  • 根据 回答

    再次,我们用它 is_bound 来检查表单是否能够通过验证。请参阅 文档的这一部分 :

    装订表格和非装订表格

    表单实例要么绑定到一组数据,要么不绑定。

    • 如果它绑定到一组数据,它就能够验证该数据并将数据显示在 HTML 中,并将表单呈现为 HTML。
    • 如果未绑定,它就无法进行验证(因为没有数据需要验证!),但它仍然可以将空白表单呈现为 HTML。

    我们使用元组列表来表示表单对象及其详细信息,以实现更高的可扩展性和更少的重复性。

    我们不会重写 , get() 而是重写 get_context_data() ,使将表单的新空白实例(带前缀)插入到响应中成为任何请求的默认操作。在 POST 请求的上下文中,我们重写该 post() 方法以:

    1. 使用 prefix 检查每个表单是否已提交
    2. 验证已提交的表单
    3. 使用 cleaned_data
    4. 数据 context 将所有无效的表单返回到响应中
    # views.py
    
    class MultipleForms(TemplateResponseMixin, ContextMixin, View):
    
        form_list = [ # (context_key, formcls, prefix)
            ("form_a", FormA, "prefix_a"),
            ("form_b", FormB, "prefix_b"),
            ("form_c", FormC, "prefix_c"),
            ...
            ("form_x", FormX, "prefix_x"),
        ]
    
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            # Add blank forms to context with prefixes
            for context_key, formcls, prefix in self.form_list:
                context[context_key] = formcls(prefix=prefix)
            return context
    
        def post(self, request, *args, **kwargs):
            # Get object and context
            self.object = self.get_object()
            context = self.get_context_data(object=self.object)
            # Process forms
            for context_key, formcls, prefix in self.form_list:
                if prefix in request.POST:
                    # Get the form object with prefix and pass it the POST data to \
                    # validate and clean etc.
                    form = formcls(request.POST, prefix=prefix)
                    if form.is_bound:
                        # If the form is bound (i.e. it is capable of validation)  \
                        # check the validation
                        if form.is_valid():
                            # call the form's save() method or do whatever you     \
                            # want with form.cleaned_data
                            form.save()
                        else:
                            # overwrite context data for this form so that it is   \
                            # returned to the page with validation errors
                            context[context_key] = form
            # Pass context back to render_to_response() including any invalid forms
            return self.render_to_response(context)
            
    

    @ybendana 的答案 不起作用 .

    类中,将 Mixin 对象作为属性并像上面一样进行挂钩, form_list get_context_data() 不会花费太多工作量 post()

    编辑:这已经存在。请参阅 此存储库 .

    注意: 此方法需要 TemplateResponseMixin render_to_response() 才能 ContextMixin 工作 get_context_data() 。请使用这些 Mixin 从它们派生的 CBV

  • 您能解释一下您的答案吗?这将有助于其他遇到类似问题的人,并有助于调试您或提问者的代码……

  • 看法:

    class AddProductView(generic.TemplateView):
    template_name = 'manager/add_product.html'
    
        def get(self, request, *args, **kwargs):
        form = ProductForm(self.request.GET or None, prefix="sch")
        sub_form = ImageForm(self.request.GET or None, prefix="loc")
        context = super(AddProductView, self).get_context_data(**kwargs)
        context['form'] = form
        context['sub_form'] = sub_form
        return self.render_to_response(context)
    
    def post(self, request, *args, **kwargs):
        form = ProductForm(request.POST,  prefix="sch")
        sub_form = ImageForm(request.POST, prefix="loc")
        ...
    

    模板:

    {% block container %}
    <div class="container">
        <br/>
        <form action="{% url 'manager:add_product' %}" method="post">
            {% csrf_token %}
            {{ form.as_p }}
            {{ sub_form.as_p }}
            <p>
                <button type="submit">Submit</button>
            </p>
        </form>
    </div>
    {% endblock %}
    
  • 这有点晚了,但这是我找到的最佳解决方案。你为表单名称及其类创建一个查找字典,还必须添加一个属性来标识表单,并且在视图中必须将其添加为隐藏字段,并使用 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)
    

    我希望这对将来有帮助。

  • 我也遇到了同样的问题,并试图找到一种方法来在单独的表单视图中处理每个帖子,然后重定向到通用模板视图。关键是让模板视图负责获取内容,让表单视图负责保存。但验证是一个问题。我曾想过将表单保存到会话中……仍在寻找一个干净的解决方案。

  • Django 的基于类的视图提供了一个通用的 FormView,但出于所有意图和目的,它被设计为仅处理一种表单。

    使用 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>
    
    ...
    
  • 虽然这可能不是最优雅和可扩展的解决方案,但它绝对非常简单且易于实现。而且是迄今为止速度最快的。我已经在自定义 CBV 中实现了它,效果非常好。好主意!

  • atx 2月前 0 只看Ta
    引用 14

    想要分享我的解决方案,其中没有使用 Django Forms。我在单个页面上有多个表单元素,我想使用单个视图来管理来自所有表单的所有 POST 请求。

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

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

    视图.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
    
  • 使用单个

    tag like this means required fields are required globally when they should be per-form depending on which submit button was clicked. Splitting into two
    tags (with the same action) works.
  • 这是一个很好的解决方案。我需要更改 __get_form 的一行才能使其正常工作:data = request.POST if prefix in next(iter(request.POST.keys())) else None 否则不起作用。

  • @AndréTerra 绝对可以,尽管您可能希望将它放在从 TemplateView 继承的通用类中,以便可以在其他视图中重用它。

  • 我非常喜欢这个解决方案。有一个问题:为什么 _get_form() 不是 MyView 类的方法?

  • 我需要在同一页面上独立验证的多个表单。我遗漏的关键概念是 1) 使用表单前缀作为提交按钮名称和 2) 无界表单不会触发验证。如果它对其他人有帮助,以下是我使用 TemplateView 的两个表单 AForm 和 BForm 的简化示例,基于 @adam-nelson 和 @daniel-sokolowski 的回答以及 @zeraien 的评论( https://.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>
    
  • 将此模式扩展到三种表单的情况会不会很笨拙?例如,先检查第一个表单的 is_valid(),然后再检查前两个表单,等等……也许只需让treated = False,当找到兼容的表单时将其更新为True?

返回
作者最近主题: