2

I'm trying to implement three forms on the same page in Django. Each form has its own submit button as shown in my code. When I submit data on any one form, the data is saved but the other forms go blank upon the resulting refresh, and I get a validation error on the other forms if a field was required, for example.

My hunch is that the way Django handles the POST and subsequent page refresh is what's causing the problem, and it's trying to validate all forms regardless of which submit button I press. I thought mapping the button names as shown would prevent this but clearly not, and perhaps I'll have to handle each form individually with AJAX to solve this. However I'm not SURE that's the fix, and would love it if someone could shed light on what's really happening (i.e., explain what Django is trying to do upon submit) so I can better understand how to solve. Any help is extremely appreciated. Handling multiple forms in one view in Django is anything but straightforward.

Here's my code:

Views.py

def update_machine_view(request, brand_name_slug, mclass_slug, profile_slug):

    machineentry = MachineEntry.objects.prefetch_related().select_related().get(profile_slug=profile_slug)

    f_plate         = PlateForm(request.POST or None, instance=machineentry.plate)
    f_dimensions    = DimensionsForm(request.POST or None, instance=machineentry.dimensions)
    f_chassis       = ChassisForm(request.POST or None, instance=machineentry.chassis)

    if request.method == 'POST' and 'save_plate' in request.POST:
        if f_plate.is_valid():
            f_plate.save()

    if request.method == 'POST' and 'save_dimensions' in request.POST:
        if f_dimensions.is_valid():
            f_dimensions.save()

    if request.method == 'POST' and 'save_chassis' in request.POST:
        if f_chassis.is_valid():
            f_chassis.save()


    context = {
        'f_plate': f_plate,
        'f_dimensions': f_dimensions,
        'f_chassis': f_chassis,
        'obj': machineentry,
    }

    return render(request, "machines/update_machine_form.html", context)

Template:



<form method="post">
   {% csrf_token %}
   {{ f_plate | crispy }}
   <button type='submit' name='save_plate'>Save</button>
</form>
<form method="post">
   {% csrf_token %}
   {{ f_dimensions | crispy }}
   <button type='submit' name='save_dimensions'>Save</button>
</form>
<form method="post">
   {% csrf_token %}
   {{ f_chassis | crispy }}
   <button type='submit' name='save_chassis'>Save</button>
</form>

Milo Persic
  • 985
  • 1
  • 7
  • 17
  • 1
    I can confirm that I have not come across any "proper" solution for submitting multiple forms on one page without using AJAX. – devdob Jan 14 '20 at 04:24

1 Answers1

8

What I can understand is that you are passing request.POST with every form even the ones which aren't submitted. But in request.POST, you have the values for the form you submitted, not the other ones. Hence the error is showing field is required. I suggest you to do like this:

def update_machine_view(request, brand_name_slug, mclass_slug, profile_slug):

    machineentry = MachineEntry.objects.select_related('plate', 'dimensions', 'chassis').get(profile_slug=profile_slug)

    f_plate         = PlateForm(instance=machineentry.plate)
    f_dimensions    = DimensionsForm(instance=machineentry.dimensions)
    f_chassis       = ChassisForm(instance=machineentry.chassis)

    if request.method == 'POST':
        if 'save_plate' in request.POST:
             f_plate = PlateForm(request.POST, instance=machineentry.plate)
             if f_plate.is_valid():
                 f_plate.save()

        if 'save_dimensions' in request.POST:
            f_dimensions = DimensionsForm(request.POST, instance=machineentry.dimensions)
            if f_dimensions.is_valid():
               f_dimensions.save()

        if 'save_chassis' in request.POST:
             f_chassis = ChassisForm(request.POST, instance=machineentry.chassis)
             if f_chassis.is_valid():
                  f_chassis.save()


    context = {
        'f_plate': f_plate,
        'f_dimensions': f_dimensions,
        'f_chassis': f_chassis,
        'obj': machineentry,
    }

    return render(request, "machines/update_machine_form.html", context)
ruddra
  • 50,746
  • 7
  • 78
  • 101
  • 1
    Thank you for this excellent solution, you really helped me! Also I appreciate devbob's perspective above, thanks for that! I think I'm going to do ajaxify these forms, but now I know how to handle the post in their present state. – Milo Persic Jan 15 '20 at 01:01