8

How do I use more than one form per page in Django?

Herman Schaaf
  • 39,417
  • 19
  • 92
  • 137
rajkumar
  • 105
  • 1
  • 1
  • 8
  • 1
    Please provide more information, otherwise we cannot help you. – Felix Kling May 02 '11 at 12:55
  • 1
    possible duplicate of [Proper way to handle multiple forms on one page in Django](http://stackoverflow.com/questions/1395807/proper-way-to-handle-multiple-forms-on-one-page-in-django) – Anto Jul 17 '13 at 13:21

2 Answers2

15

Please see the following previously asked (and answered) questions:

Django: multiple models in one template using forms

and

Proper way to handle multiple forms on one page in Django.

Depending on what you're really asking, this is my favorite way to handle different models on the same page:

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()
Community
  • 1
  • 1
Herman Schaaf
  • 39,417
  • 19
  • 92
  • 137
  • thank you for your explanation. In my case, I have two models (Parent & child) and rendering both forms in a single template. When I'm submitting the form, parent.is_valid() is returning true but child.is_valid() is false and the error says "This field can not be empty" where the "This field" is my foreign key model field in child Model – Shahriar Rahman Zahin May 28 '20 at 11:37
5

Judging by the date of your questions I am going to assume you are using class based views, below is copied from my other answer here: Proper way to handle multiple forms on one page in Django

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)

The html template is to the following effect:

...

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

...
Community
  • 1
  • 1
Daniel Sokolowski
  • 10,545
  • 3
  • 61
  • 49
  • This: `data = self.request.POST if 'NegotiationAttachment-submit' in self.request.POST else None`!!!! – zeraien Mar 27 '15 at 01:54
  • @zeraien ? - can you clarify – Daniel Sokolowski Mar 27 '15 at 20:45
  • 2
    When having multiple forms on the same page, adding a key/value to the submit button was what allowed me to differentiate the different forms in the view processing code. So in my case I created a little method that I call for each form... `def _get_post(request, prefix): return request.POST if '%s-submit' % prefix in request.POST else None` and each form just calls that to get the POST data: `password_form = PasswordChangeForm(user=request.user, data=_get_post(request, 'password'), prefix='password')` That line gave me the inspiration to do that ;-) – zeraien Mar 28 '15 at 22:05