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

В моем Django проекте есть приложение, регистрирующее продажи, которое должно иметь несколько форм, сохраняемых одновременно с помощью Django formsets. Мне удалось заставить динамические формы работать, но это требует улучшения, чтобы при выборе продукта в каждой форме, цена продукта могла динамически отображаться в поле price, а общая цена (product_price * quantity) могла также отображаться в соответствующем поле. Мой код показывает только цену первой формы, но когда я добавляю новые формы (динамически), все они отображаются с ценой первой формы, а не с ценой продукта, выбранного в каждой форме. Пожалуйста, мне очень нужна помощь, чтобы исправить это.

Мои модели для продажи...

class Product(models.Model):
slug = models.SlugField(editable=False, unique=True, verbose_name=_("product id"))
is_active = models.BooleanField(default=True, verbose_name=_("is active"))
product_name = models.CharField(max_length=50, verbose_name=_("product name"))
cost_price = models.DecimalField(max_digits=19, decimal_places=2, verbose_name=_("cost price"))
retail_price = models.DecimalField(max_digits=19, decimal_places=2, verbose_name=_("retail price"))

class ProductSale(models.Model):
slug = models.SlugField(editable=False, unique=True, verbose_name=_("product sale id"))
product = models.ForeignKey(Product, on_delete=models.PROTECT, verbose_name=_("product"))
retail_price = models.DecimalField(max_digits=19, decimal_places=2, blank=True, null=True, verbose_name=_("retail price"))
quantity = models.SmallIntegerField(verbose_name=_("quantity"))
total = models.DecimalField(max_digits=19, decimal_places=2, blank=True, null=True, verbose_name=_("total"))

Форма...

ProductSaleFormset = modelformset_factory(
ProductSale,
fields=("product", "quantity"),
extra=1,
localized_fields="__all__",
widgets={
    "product": forms.Select(
        attrs={
            "id": "id_product",
            "class": "form-control",
            "style": "width:350px",
        }
    ),
    "quantity": forms.NumberInput(
        attrs={
            "id": "id_quantity",
            "class": "form-control",
            "style": "width:180px",
        },
    ),
},

)

Сценарий...

<script type="text/javascript">

function getProductPrice() {
   $('#id_product').change(function(){
  var product = $(this).val();
  console.log(product);
  $.ajax({
      url : "{% url 'stock:product-retail-price' %}",
      type : "GET",
      dataType: "json",
      data : {
          "product" : product,
          },
      success: function(json) {
          document.getElementById('id_product_price').value=json.retail_price;
          document.getElementById('id_total_price').value=json.total_price;
      },
      failure: function(json) {
          alert('Got an error dude');
      }
  });
});
};


function updateElementIndex(el, prefix, ndx) {
    var id_regex = new RegExp('(' + prefix + '-\\d+)');
    var replacement = prefix + '-' + ndx;
    if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
    if (el.id) el.id = el.id.replace(id_regex, replacement);
    if (el.name) el.name = el.name.replace(id_regex, replacement);


}
function cloneMore(selector, prefix) {
    var newElement = $(selector).clone(true);
    var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
    newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
        var name = $(this).attr('name')

        if(name) {

            name = name.replace('-' + (total-1) + '-', '-' + total + '-');
            var id = 'id_' + name;
            $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');

        }
    });
    newElement.find('label').each(function() {
        var forValue = $(this).attr('for');
        if (forValue) {

          forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
          $(this).attr({'for': forValue});

        }
    });
    total++;
    $('#id_' + prefix + '-TOTAL_FORMS').val(total);
    $(selector).after(newElement);
    var conditionRow = $('.form-col:not(:last)');
    conditionRow.find('.btn.add-form-col')
    .removeClass('btn-success').addClass('btn-danger')
    .removeClass('add-form-col').addClass('remove-form-col')
    .html('-');

    return false;
}
function deleteForm(prefix, btn) {
    var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
    if (total > 1){
        btn.closest('.form-col').remove();
        var forms = $('.form-col');
        $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
        for (var i=0, formCount=forms.length; i<formCount; i++) {
            $(forms.get(i)).find(':input').each(function() {
                updateElementIndex(this, prefix, i);
            });
        }
    }
    return false;
}
$(document).on('click', '.add-form-col', function(e){
    e.preventDefault();
    cloneMore('.form-col:last', 'form');
    return false;
});
$(document).on('click', '.remove-form-col', function(e){
    e.preventDefault();
    deleteForm('form', $(this));
    return false;
});


$(document).ready(
   getProductPrice()
);

</script>

Вид ajax...

from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.views.decorators.http import require_GET
from .models import Product

@login_required
@require_GET
def get_product_retail_price(request):
    product_id = request.GET["product"]
    product_quantity = request.GET.get("id_quantity")
    if product_id:
        try:
            retail_price = Product.objects.get(id=product_id).retail_price
            total_price = retail_price * product_quantity if product_quantity else 0
            response_data = {"retail_price": retail_price, "total_price": total_price}
            return JsonResponse(response_data)
        except Product.DoesNotExist:
            pass
    return JsonResponse({"status": "error"})
Вернуться на верх