Отправка результатов вычислений на Javascript в бэкенд Django

У меня есть HTML-форма, в которой пользователи вводят название элементов и соответствующее ему значение в форму ввода, что отражается при отправке формы на бэкенд Django.

В моей HTML-форме я включил несколько Javascript, чтобы общее количество этих значений отражалось мгновенно без обновления и даже перед отправкой формы.

Моя цель - отправить общую сумму, отраженную в HTML, в связанный класс Model после отправки. Обратите внимание, что причина вопроса в том, что итоговые значения не добавляются вручную, они напрямую вычисляются с помощью Javascript.

Вот пример, чтобы сделать вещи более понятными.

Вот HTML-шаблон:

                    <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>

Вот 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>

Вот где итог отражается в шаблоне HTML

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

Вот 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')

Вот мнения:

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, })

Я предполагаю, что ваш вопрос заключается в том, как получить значение в этом элементе:

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

Вы можете просто добавить элемент input в ваш html код и добавить в него имя:

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

Затем в вашем файле views.py:

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

Вы также можете вручную получить Итого:

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

Возможно, то, что вы ищете, это <input type="hidden" ...>, он не виден конечному пользователю и включен в форму submit

Обновить html до:

<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>

Обновить сценарий, чтобы назначить resultGroupSet1 как:

  • текстовое содержимое в тег span с id=Total
  • значение в скрытый ввод с 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;

Никаких других изменений в представлениях.

Когда используется поле ввода с символом name="total_assets", значение будет передано в тело запроса и будет доступно по адресу request.POST. Здесь, поскольку поле total_assets скрыто, оно не видно пользователям, и все же значение доступно в POST-данных, когда форма отправлена. Таким образом, когда вызывается form.save(), вычисленное значение (с помощью JS) будет сохранено.

Почему бы не сделать этот расчет в Django Backend?

Мое предложение - передать все аргументы нормально и просто добавить слушателя для сохранения модели (каждый раз, когда вы будете сохранять элемент в таблице, этот маленький кусочек кода будет выполняться прямо перед сохранением):

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

Таким образом, когда вы захотите сохранить такой элемент в другом месте (например, в бэкенде), вам не придется переписывать код, который это делает, а просто сохранить экземпляр.

Подробнее о функции pre_save сигналов можно прочитать здесь

Вернуться на верх