2

I'm trying to learn Django and am finding it frustratingly difficult to do fairly basic things. I have the following form classes:

from django import forms

class InputForm(forms.Form):
  field1 = forms.FloatField(label="First field: ")
  field2 = forms.FloatField(label="Second field: ")

class OutputForm(forms.Form):
  outfield = forms.FloatField(label="Result: ")

And the following template:

<form>
    {{ input_form.as_ul }}
    <input type="submit" class="btn" value="Submit" name="submit_button">
</form>

<form>
    {{ output_form.as_ul }}
</form>

And finally, the following view:

from django.shortcuts import render
from .forms import InputForm, OutputForm

def index(request):

    if request.GET.get('submit_button'):
        # ?????

    else:
        input_form = InputForm()
        output_form = OutputForm()
        return render(request, 'index.html', {'input_form': input_form, 
                                            'output_form': output_form})

All I want to happen is that when I hit the submit button, the values in first field and second field get added and displayed in the result field. I know from debug outputs that the ????? commented block is run when I press the button, but I have so far not been able to figure out what to do to actually access or alter the data in my fields. I don't care about keeping a record of these transactions so it feels like storing these in a database is tremendous overkill. What is the correct way to approach this?

3 Answers3

1

There's a few things at work here, lets go through them one by one.

1. Calculating the output value

Use the request data to build the form, validate it, and then calculate the sum:

if request.GET.get('submit_button'):
    input_form = InputForm(request.GET)
    if input_form.is_valid():
       data = input_form.cleaned_data
       result = data["field1"] + data["field2"]

2. Read-only output

It's not clear why you're using a submittable form field for data that is to be calculated by your app, as opposed to playing the result into a regular html 'label' in the template. If you really want/need to set the result in a form field, I'd at least make it read-only. See In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited? for an example of how to do that.

3. Set the output data in the form

Set the output form/field's initial data to the result you have calculated. following on form the code at (1):

       result = data["field1"] + data["field2"]
       output_form = OutputForm(initial={"outfield": result})

then render the view as you're already doing:

       return render(
           request,
           'index.html',
           {'input_form': input_form, 'output_form': output_form},
       )
Tom Dalton
  • 6,122
  • 24
  • 35
  • I originally had that field as readonly but took it out because I wanted to cut this code down to the very basics. Beyond that, for some reason I'm unable to get into the `if input_form.is_valid():` block. What would cause these fields to evaluate as invalid? – Proven Paradox Feb 23 '18 at 16:45
  • take a look at https://docs.djangoproject.com/en/2.0/ref/forms/api/#django.forms.Form.errors.as_data e.g. `input_form.errors.as_data()`. That said, if you render your form (even when it has errors) then the errors should be nicely rendered too. – Tom Dalton Feb 23 '18 at 16:59
  • 1
    Oh I noticed your manually-rendered form is broken - it looks like your submission button is part of a differnet form. You'll need the submit button to be part of the same form you have the `{{ input_form.as_ul }}` in. – Tom Dalton Feb 23 '18 at 17:02
  • I didn't realize it was rendering the errors because it was just that the fields were required. Which... is super confusing, because that shouldn't be a problem when I filled the forms, right? I have some more research to do. – Proven Paradox Feb 23 '18 at 17:02
  • Ah, so the button can't be separate. I find that counterintuitive, but if that's what the framework needs then I will adjust my expectations. Thanks for your assistance, this is working now. I'll edit my original template to be correct for future reference. If someone else encounters a similar problem it'd be better that they not get confused by my button placement I think. – Proven Paradox Feb 23 '18 at 17:06
0

You don't need a form to display the result. When the form is valid, you can get the values from the form's cleaned_data and calculate result.

def index(request):
    result = None  # Set to None by default

    if request.GET.get('submit_button'):  # The submit button was pressed
        input_form = InputForm(request.GET)  # Bind the form to the GET data
        if input_form.is_valid():
            result = input_form.cleaned_data['field1'] + input_form.cleaned_data['field2']  # Calculate the result
    else:
        input_form = InputForm()  # Submit button was not pressed - create an unbound (blank) form
    return render(request, 'index.html', {'input_form': input_form, result:result})

Then in your template, include {{ result }} instead of the output form.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • Doesn't this fail to return anything when the button is pressed? Or did you mean for the last line with the return to be outside the if/else? Altering a bit to get a return in both cases, I get the same invalid field problem I mentioned commenting on @Tom Dalton's answer. – Proven Paradox Feb 23 '18 at 16:51
  • The last line was indented incorrectly. It sounds like you might have got the ‘invalid field’ error because the submit button was integrated wrong framework. Note that’s not a limitation of Django, it’s the way html works. – Alasdair Feb 24 '18 at 10:17
0

Setting the initial poperty on a field prefills it:

output_form.fields['outfield'].initial = input_form.data.get ('field1') + input_form.data.get ('field1')

You need to pass request.POST or request.GET into OutputForm when you instantiate it. You should also be calling is_valid() on the from and using cleaned_data instead of data after you check if it is valid.

kagronick
  • 2,552
  • 1
  • 24
  • 29