Unable to POST Values And Create a Derived Value in Django

I am new to Django and still learning. Currently, I have a running code below which allows me to input data but I am unable to POST it to DB. I am not sure what I’ve done wrong.

Model

class ManHour(models.Model):

    station_choices = (
       ('alpha','A'),
       ('beta', 'B'),
       ('gamma','G'),
       )


    station = models.CharField(
        max_length=3,
        choices=station_choices,
    )

    date      = models.DateTimeField(auto_now=True)
    ops_1 = models.DecimalField(max_digits= 5, decimal_places= 3, default = 1)
    ops_2 = models.DecimalField(max_digits= 5, decimal_places= 3, default = 1)
    ops_3 = models.DecimalField(max_digits= 5, decimal_places= 3, default = 1)

Form

from django import forms
from webapp.models import ManHour

class InputForm(forms.ModelForm):
    class Meta:
        model = ManHour
        fields = ['station', 'ops_1', 'ops_2', 'ops_3']

Views

def form_page(request):
    if request.method == 'POST':
        properties_Form = InputForm(request.POST)
        if properties_Form.is_valid():
            properties_Form.save()

            return redirect('home')
    context ={}
    context.update(csrf(request))
    context['request'] = request
    context['form']= InputForm()
    return render(request, "form.html", context)

HTML

{% block content %}

<form target="upload_frame" action="" method="post" enctype="multipart/form-data" >
    {% csrf_token %}
       {{ form.as_p }}<br>
       <input type="submit" name="submit" value="Upload" id="submit">
</form>

{% endblock %}

Given the above code I am not sure why I am unable to post values to DB.

My second question is that I want to create a derived field that sums ops1 and ops2 and I want it show to user. I tried adding another field using code below but I am getting error of unrecognized fields in save function.

class InputForm(forms.ModelForm):
    class Meta:
        model = ManHour
        fields = ['station', 'ops_1', 'ops_2', 'ops_3']

    def save(self, commit=True):
        self.total_ops = self.ops_1 + self.ops_2
    return super(InputForm, self).save()

I am getting an error instance of InputForm has no ops_1 member.

So I’ve two two question

  • Why am I unable to make POSTs to DB?
  • How can I create an evaluated field and show user?

Update

Scripts after applying changes:

Model

from django.db import models

class ManHour(models.Model):

    station_choices = (
       ('alpha','A'),
       ('beta', 'B'),
       ('gamma','G'),
       )


    station = models.CharField(
        max_length=3,
        choices=station_choices,
    )

    date  = models.DateTimeField(auto_now=True)
    ops_1 = models.DecimalField(max_digits= 5, decimal_places= 3, default = 1)
    ops_2 = models.DecimalField(max_digits= 5, decimal_places= 3, default = 1)
    ops_3 = models.DecimalField(max_digits= 5, decimal_places= 3, default = 1)

Form

from django import forms
from webapp.models import ManHour

class InputForm(forms.ModelForm):
    class Meta:
        model = ManHour
        fields = ['station', 'ops_1', 'ops_2', 'ops_3']

Admin

from django.contrib import admin
from .models import ManHour
# Register your models here.

admin.site.register(ManHour)

Views

from django.shortcuts import render, redirect
from django.template.context_processors import csrf
from django.http import HttpResponse
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import login, logout, authenticate 
from django.contrib import messages
from .form import InputForm
from django.urls import reverse
from django.contrib.auth.decorators import login_required



# Create your views here.

def home_view(request, **kwargs):
    return render(request, 'home.html', {})

def login_request(request):
    if request.method == 'POST':
        form = AuthenticationForm(request=request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                messages.info(request, f"You are now logged in as {username}")
                return redirect('form')
            else:
                messages.error(request, "Invalid username or password.")
        else:
            messages.error(request, "Invalid username or password.")
    form = AuthenticationForm()
    return render(request = request,
                    template_name = "login.html",
                    context={"form":form})
                    
@login_required(login_url='login')
def form_page(request):
    if request.method == 'POST':
        properties_Form = InputForm(request.POST)
        if properties_Form.is_valid():
            instance = properties_Form.save()
            total_ops = instance.ops_1 + instance.ops_2
            redirect("%s?total_ops=%s" % (reverse('home'), total_ops))
    context = {}
    context['request'] = request
    context['form']= InputForm()
    return render(request, "form.html", context)

base.HTML

<!DOCTYPE html>
<html>
<head>
    <title>Budget Data</title>
    <meta charset="utf-8" />
    {% load staticfiles %}
    <link rel='stylesheet' href="{% static 'css/bootstrap.min.css'%}", type="text/css" />
</head>
<body>
    {% block content %} 
    {% endblock %}
</body>

</html>

login.HTML

{% extends 'base.html' %}

{% block title %}Login{% endblock %}

{% block content %}
  <h2>Login</h2>
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Login</button>
  </form>
{% endblock %}

form.html

{% extends 'base.html' %}

{% block title %}Form{% endblock %}

{% block content %}

<form target="upload_frame" action="" method="post" enctype="multipart/form-data" >
    {% csrf_token %}
       {{ form.as_p }}<br>
       <input type="submit" name="submit" value="Upload" id="submit">
</form>

{% endblock %}

URLS.py

from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path
from pages import views

urlpatterns = [
    path('home/', views.home_view, name='home'),
    path('admin/', admin.site.urls),
    path('login/', views.login_request, name='login'),
    path('form/', views.form_page, name='form'),

I’ve updated the question to make changes as per the answer, however I am unable to view the evaluated field and I am unable to POST data to db.

Answer

If you want to get only data of form then you can access its through cleaned_data(Django Docs).

class InputForm(forms.ModelForm):
    class Meta:
        model = ManHour
        fields = ['station', 'ops_1', 'ops_2', 'ops_3']

    def save(self, commit=True):
        self.total_ops = self.cleaned_data.get("ops_1", 0) + self.cleaned_data.get("ops_2", 0)
        return super(InputForm, self).save(commit=commit)

If you wand to modify instance first of all you need to call super(InputForm, self).save(commit=False) (Django Docs) method to get instance.


Also your form can’t be valid. Because station field has max_length=3. And some choices are length 4 and 5. It must be:

station = models.CharField(
    max_length=5,
    choices=station_choices,
)

Update

If you want to do some calculation with the form data and provide it to the home view you can use GET parameters for that, like the following:

from django.urls import reverse

def form_page(request):
    context = {}
    try:
        man_hour = ManHour.objects.get(pk=request.GET.get("pk"))
    except ManHour.DoesNotExist:
        man_hour = None

    if man_hour:
        context["total_ops"] = man_hour.ops_1 + man_hour.ops_2

    if request.method == 'POST':
        properties_Form = InputForm(request.POST, instance=man_hour)
        if properties_Form.is_valid():
            obj = properties_Form.save()
            return redirect("%s?pk=%s" % (reverse('form'), obj.pk))
    else:
        context['form']= InputForm(instance=man_hour)

    return render(request, "form.html", context)

In HTML add url action to the form tag to prevent opening in new tab and for calculation you can use jQuery:

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" async></script>
    </head>
    <body>
        <p>This page is loaded with jquery.</p>
      

        <form target="upload_frame" action="." method="post" enctype="multipart/form-data" >
            {% csrf_token %}
            {{ form.as_p }}<br>
            <input type="text" name="total_ops" value="{{ total_ops }}" disabled><br>
            <input type="submit" name="submit" value="Upload" id="submit">
        </form>


        <script>
            $(document).ready(function() {
                $('input[name="ops_1"],input[name="ops_2"],input[name="ops_3"]').change(function() {
                    let ops_1 = parseFloat($('input[name="ops_1"]').val());
                    let ops_2 = parseFloat($('input[name="ops_2"]').val());
                    let ops_3 = parseFloat($('input[name="ops_3"]').val());
                    $('input[name="total_ops"]').val(ops_1 + ops_2 + ops_3);

                })
            });
        </script>
    </body>
</html>

Actually you don’t need to override the save() method of ModelForm.