Home > Blockchain >  Pass Javascript Calculations to Django Backend
Pass Javascript Calculations to Django Backend

Time:10-09

I have an HTML form where users type in the name of items and value corresponding to it in an input form, which is reflected when the form is submitted to Django backend.

In my HTML form I have included some Javascript so that the total of these values are reflected instantly without refreshing and even before submitting the form.

My goal:

Send the total amount calculated by Javascript in the HTML under id Total

                        <th scope="col">Total Equipment and Assets</th>
                        <th scope="col" id="Total"></th>

to the class in the

total_assets= models.IntegerField(null=True, blank=True, verbose_name='Total Assets')

in the Models.py after submitting.

Note that the reason for the question is that the total values are not manually added they are directly calculated using Javascript.

Here is a sample to make things more clear.

Here is the HTML Template:

                    <tr>
                      <td>
                        <input
                          placeholder="Type in the Equipment and assets"
                          type="text"
                          class="form-control"
                          name="item_1"
                          id="item_1"
                          {% if form.is_bound %}value="{{ form.item_1.value }}"{% endif %}/>
                          {% for err in form.item_1.errors %}
                            <small class="text-danger mb-2 ml-2">{{ err }}</small>
                          {% endfor %}
                      </td>
                      <td>
                        <h6 style="float:left; margin-right:5px; margin-top:7px">$</h6>
                        <input
                          type="number"
                          class="form-control w-25 subtotal-group subtotal-group-1"
                          name="item_1_amount"
                          id="item_1_amount"
                          {% if form.is_bound %}value="{{ form.item_1_amount.value }}"{% endif %}/>
                          {% for err in form.item_1_amount.errors %}
                            <small class="text-danger mb-2 ml-2">{{ err }}</small>
                          {% endfor %}
                      </td>
                    </tr>
                    <tr>
                      <td>
                        <input
                          placeholder="Type in the Equipment and assets"
                          type="text"
                          class="form-control"
                          name="item_2"
                          id="item_2"
                          {% if form.is_bound %}value="{{ form.item_2.value }}"{% endif %}/>
                          {% for err in form.item_2.errors %}
                            <small class="text-danger mb-2 ml-2">{{ err }}</small>
                          {% endfor %}
                      </td>
                      <td>
                      <h6 style="float:left; margin-right:5px; margin-top:7px">$</h6>
                      <input
                        autocomplete="off"
                        type="number"
                        class="form-control w-25 subtotal-group subtotal-group-1"
                        name="item_2_amount"
                        id="item_2_amount"
                        {% if form.is_bound %}value="{{ form.item_2_amount.value }}"{% endif %}/>
                        {% for err in form.item_2_amount.errors %}
                          <small class="text-danger mb-2 ml-2">{{ err }}</small>
                        {% endfor %}
                    </td>
                    </tr>

Here is the Javacript

    <script>
    const q=(e,n=document)=>n.querySelector(e);
    const qa=(e,n=document)=>n.querySelectorAll(e);
    const results={};
    console. log(results)
    qa('[type="number"].form-control').forEach(input=>input.addEventListener('input',function(e){
      results[ this.name ]=Number( this.value );
      const resultGroupSet1 = [...qa('.subtotal-group-1')]
                              .map(s => Number(s.value))
                              .reduce((a,v) => a v);
      q('th#Total').textContent = resultGroupSet1;

    }));
    </script>

Here is where the total is reflected in the HTML template

                  <thead class="table-light">
                      <tr>
                        <th scope="col">Total Equipment and Assets</th>
                        <th scope="col" id="Total"></th>
                      </tr>
                    </thead>

Here is the models.py

    item_1                      = models.CharField(max_length=100,null=True, blank=True, verbose_name='Item 1')
    item_1_amount               = models.IntegerField(null=True, blank=True, verbose_name='Item 1 Amount')
    item_2                      = models.CharField(max_length=100,null=True, blank=True, verbose_name='Item 2')
    item_2_amount               = models.IntegerField(null=True, blank=True, verbose_name='Item 2 Amount')
    total_assets                = models.IntegerField(null=True, blank=True, verbose_name='Total Assets')

Here is the views:

def add_bp(request):
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = infoForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            form.save()
            b_name = form.cleaned_data.get('bName')
            messages.success(request, f'PDF created for {b_name}!')
            return redirect('application:core')
    # if a GET (or any other method) we'll create a blank form
    else:
        form = infoForm()
    return render(request, 'application/template.html', {'form': form, })

CodePudding user response:

Update html to:

<thead class="table-light">
    <tr>
        <th scope="col">Total Equipment and Assets</th>
        <th scope="col">
            <!-- Value which is visible (calculated using JS) -->
            <span id="Total"></span>

            <!-- Add a hidden input to store value (value calculated and assigned using JS) -->
            <input type="hidden" name="total_assets" value="0" id="total_assets">
        </th>
    </tr>
</thead>

Update script to assign resultGroupSet1 as:

  • textual content to the span tag with id=Total
  • value to the hidden input with name=total_assets
// Assign result to span tag which is visible
q('span#Total').textContent = resultGroupSet1;

// Assign result as value to hidden input field with name total_assets
q('input#total_assets').value = resultGroupSet1;

No other changes in views.

As an input field with a name="total_assets" is used, the value will be passed on to the request body and will be accessible at request.POST. Here, as the total_assets field is hidden it is not visible to the users and still the value is available in POST data when form is submitted. So, when form.save() is called the calculated value (using JS) will be saved.

CodePudding user response:

I assumes your question is how to get the value in this element:

<th scope="col" id="Total"></th>

You can just simply add input element in your html code and add the name into it:

<th scope="col"><input id="Total" name="total_assets" value=""></th>

Then in your views.py:

def add_bp(request):
if request.method == 'POST':
    form = infoForm(request.POST)
    if form.is_valid():
        form.save()
        

You can also manually get the Total:

def add_bp(request):
if request.method == 'POST':
    total_assets = request.POST.get("total_assets")

CodePudding user response:

Probably what you are looking for is <input type="hidden" ...>, it's not visible by the end user and it's included in the form submit

CodePudding user response:

Why not do this calculation in Django Backend?

My suggestion is to pass all the arguments normally and just add a listener to the model saving (every time you will save an element to the table this little piece of code will run right before it saves it):

from django.db.models.signals import pre_save
from django.dispatch import receiver

@receiver(pre_save, model_class)
def form_pre_save(instance, *args, **kwargs):
    instance.total_assets = instance.item_1_amount   instance.item_2_amount

This way when you want to save this kind of element in a different place (in the backend for example) you won't have to re-write the code that does that, but rather just save the instance.

You can read more about the signals pre_save function here

  • Related