'Django form with two submit buttons . . . one requires fields and one doesn't

I think this should be a fairly straightforward question . . . I have ONE Django form with TWO different submit buttons. The first submit button is just for SAVING to the database whatever values are typed in to the form fields (so the user can come back and finish the form later if they want). I want the form fields to NOT be required when this first submit button is clicked. When the user clicks the second submit button, though, all fields should be required. Is there a way to do this? Or do I just have to duplicate the form once for each submit button?



Solution 1:[1]

The answer above works, but I liked this way better: Changing required field in form based on condition in views (Django)

I have two buttons:

<!-- simply saves the values - all fields aren't required unless the user is posting the venue -->
<input type="submit" name="mainForm" value="Save">

<!-- post the values and save them to the database - fields ARE required-->
<input type="submit" name="postVenue" value="Post Venue">

I make all form fields required=False by default and then have this in my view:

if 'postVenue' in request.POST:
    form = NewVenueForm(request.POST)
    form.fields['title'].required = True
    form.fields['category'].required = True
    # do this for every form field
                          
elif 'mainForm' in request.POST:     
    form = NewVenueForm(request.POST)

Thanks everyone!!

Solution 2:[2]

If you're manually writing the HTML for the submit buttons, you can add a name and value attribute which your Django app can use:

<button name="action" value="save">Save</button>
<button name="action" value="submit">Submit</button>

When the form is submitted, you'll be able to know which action the user intended to perform.

class MyForm(forms.Form):

    def __init__(self, data=None, *args, **kwargs):
        super(MyForm, self).__init__(data=data, *args, **kwargs)

        # store user's intended action in self.action
        self.action = data.get('action') if data else None

        # set form fields to be not required if user is trying to "save"
        if self.action == 'save':
            for field in self.fields:
                field.required = False

Solution 3:[3]

This shouldn't be technically possible using the 'required' field attribute because Django fields are set to optional/required on the initialization of your form.

Let's look at the django call stack:

  1. Views.py receives a GET-request and initializes the form using your Forms.py form class.
  2. At this point, all your fields are created with their 'required' attributes.
  3. When you click a button, Views.py gets a POST-request. It's too late to change the 'required' attribute of the fields because they were already set when the GET-request initialized your form
  4. (The POST-request returns a redirect or a new form)

If you did try changing the 'required' attribute in your POST-request and the field was empty, your form would be invalid (AKA when you call form.is_valid() it would return False).

Caveat: Then only possible workaround would be to check for the field in your POST-request and use a Django message but that's not recommended because it disrupts the Django architecture, forcing you to reload your form (instead of redirecting) and losing the user submitted data.

UPDATE:

It's possible to do this in jquery by using a hidden submit button and a regular visible button. The regular button onClick calls jquery that sets whichever fields you want to required, then triggers the hidden submit button.

<head>
  <meta charset="utf-8" />
  <script data-require="jquery" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script>
    const allFields = [''] //put all your field ids here

    $("#tableAdd").click(function () {

      // Set all your fields back to optional
      allFields.forEach(function(field) {
          $( "#" + field ).removeAttr('required');
      });

      // Set specific fields as required, 'manufacturer_date' being the field 'table_add' requires
      var requiredFields = ['manufacturer_date'];
      requiredFields.forEach(function(field) {
          $( "#" + field ).attr('required', '');
      });

      // Then trigger the hidden input field to submit the form and send the POST request
      $( "#table_add" ).trigger( "click" );

    });
  </script>
</head>

<body>
  <input id="manufacturer_date" type="date">
  <input type="button" id="tableAdd" value="Add">
  <input type="submit" name="table_add" id="table_add" hidden>
</body>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mushroomator
Solution 2 Derek Kwok
Solution 3