1

I am a beginner in django. I have created a form using inlineformset_factory with jquery.formset.js library for increasing rows. The price field uses django-smart-selects where depending on the product selected, the available prices are populated in the price field. Everything works fine for the first row but for any other new row added, the django-smart-selects price field no longer works. Also, when I delete the first row and re-add it, the django-smart-selects no longer works. I want the price field to keep working on each row depending on what product is selected. Below are my codes:

urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^chaining/', include('smart_selects.urls')),
]

models.py

class InventoryItem(models.Model):
    date = models.DateField(default=timezone.now)
    product_name = models.TextField(blank=False, unique=True)
    sales_price = models.DecimalField(_(u'Selling Price'), decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal('0.01'))])
    objects = models.Manager()
        
class Price(models.Model):
    product_id = models.ForeignKey(InventoryItem, on_delete=models.DO_NOTHING, verbose_name='Item Name')
    selling_price = models.DecimalField(_(u'Price'), decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal('0.01'))], blank=False, default=0)
    objects = models.Manager()

class Invoice(models.Model):
    customer_name = models.TextField(blank=False, unique=True)
    objects = models.Manager()

class InvoiceItem(models.Model):
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField(blank=False)
    product_id = models.ForeignKey(InventoryItem, on_delete=models.DO_NOTHING, verbose_name='Item Name')
    price = ChainedForeignKey(Price, chained_field="product_id", chained_model_field="product_id", show_all=False, auto_choose=True, sort=True, default=0)
    objects = models.Manager()

forms.py

class InvoiceItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(InvoiceItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['price'].widget.attrs['disabled'] = 'disabled'

    class Meta:
        model = InvoiceItem
        fields = [ 'invoice', 'quantity', 'product_id', 'price']

InvoiceItemFormset = inlineformset_factory(Invoice, InvoiceItem, form=InvoiceItemForm, extra=1, can_delete=True)

views.py

class InvoiceCreateView(SuccessMessageMixin, UserPassesTestMixin, ChainedSelectFormViewMixin, CreateView):
    model = Invoice
    form_class = InvoiceForm
    template_name = "website1app/invoice_form.html"
    success_url = reverse_lazy('invoice_list')
    success_message = 'New invoice successfully added'

    def get_context_data(self, **kwargs):
        context = super(InvoiceCreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['items'] = InvoiceItemFormset(self.request.POST, prefix='invoiceitem_set')
        else:
            context['items'] = InvoiceItemFormset(prefix='invoiceitem_set')
        return context
    
    def form_valid(self, form):
        context = self.get_context_data()
        formset = context['items']
        self.object = form.save()
        if self.object.id != None:
            if form.is_valid() and formset.is_valid():
                formset.instance = self.object
                formset.save()
        return super().form_valid(form)
    
    def test_func(self):
        return True

invoice_form.html

{% extends 'base_template.html' %}
{% load widget_tweaks %}


{% block title %}

{% if object %}
Update Invoice {{ object }}
{% else %}
Add New Sales Invoice
{% endif %}

{% endblock title %}

{% block main_content %}

<form method="POST" id="invoiceForm">
  {% csrf_token %}
  {{ form.media.js }}

  {% for field in form %}
  <div class="form-group">
    {{ field.errors }}
    {{ field.label_tag }}
    {% render_field field class="form-control" %}
  </div>
  {% endfor %}


  <div class="row" style="border-top:5px; border-top-style:outset; margin-top: 10px;">
    <div class="col-sm-12 callout callout-info">
      {{ items.management_form }}
      <div class="text-center">PRODUCT LIST</div>
      <div class="row">
        <div class="col"><strong>Product</strong></div>
        <div class="col"><strong>Quantity</strong></div>
        <div class="col"><strong>Price</strong></div>
      </div>
      {% for item in items %}
      <div class="form-group row items-group">
        <div class="col">{{ item.product_id | add_class:"form-control"}}</div>
        <div class="col">{{ item.quantity | add_class:"form-control"}}</div>
        <div class="col">{{ item.price | add_class:"form-control"}}</div>
      </div>
      {% endfor %}
    </div>
  </div>


  <div style="text-align: center;">
    {% if object %}
    <input type="submit" class="btn btn-primary" value="Update Record">
    {% else %}
    <input type="submit" class="btn btn-primary" value="Save">
    {% endif %}
  </div>
</form>

<hr>

{% endblock main_content %}


{% block custom_js %}
<script>
  $(function() {

    $(".items-group").formset({
        prefix: '{{ items.prefix }}',
        deleteText: "<div class='btn btn-danger btn-circle'><i class='fas fa-trash'></i></div>",
        addText: "<div class='btn btn-success btn-circle' style='text-align: center; margin-bottom: 10px;'><i class='fas fa-plus'></i></div>",
      })

  })
</script>
{% endblock custom_js %}
Nima Derakhshanjan
  • 1,380
  • 9
  • 24
  • 37

0 Answers0