-1

I have got a form with a formset that allows me to add some lines to it. I can add some simple lines. But I also wanted to add some products from a product catalog.

So the process will be as following:

  1. User create a new purchase order (Working)
  2. User add lines as explained below (Working)
  3. User a button add lines from existing product catalog. The user will then browse or filter the product, he wants to order and add it to the purchase order.

I know what I am trying to do is wrong as I guess we can not have two def get_context_data(self, **kwargs): in the function. What would be the idea to be able to add products from the catalog in my formset?

Many Thanks,

Product list

models.py

class BaseProduct(models.Model):
    """
    Abstract base product model class providing the base fields and methods
    """
    
    supplier = models.CharField(max_length=300, blank=True, null=True)   
    manufacturer = models.CharField(max_length=300, blank=True, null=True)
    product_range = models.CharField(max_length=300, blank=True, null=True)
    part_number = models.CharField(_('Item Code'), max_length=255, blank=True, null=True)
    description = models.CharField(_('description'), max_length=255, blank=True, null=True)

Orders views.py

class OrderCreate(CreateView):
    model = Order
    template_name = 'accounting/orders/create_order.html'
    form_class = OrderForm
    success_url = None

    def get_context_data(self, **kwargs):
        data = super(OrderCreate, self).get_context_data(**kwargs)
        if self.request.POST:
            data['lines'] = OrderLineFormSet(self.request.POST)
        else:
            data['lines'] = OrderLineFormSet()
        return data

    def get_context_data(self, **kwargs):
        data = super(OrderCreate, self).get_context_data(**kwargs)
        if self.request.POST:
            data['productlines'] = OrderProductLineFormSet(self.request.POST)
        else:
            data['productlines'] = OrderProductLineFormSet()
        return data



    def form_valid(self, form):
        context = self.get_context_data()
        lines = context['lines']
        with transaction.atomic():
            form.instance.created_by = self.request.user
            self.object = form.save()
            if lines.is_valid():
                lines.instance = self.object
                lines.save()
        return super(OrderCreate, self).form_valid(form)

    def get_success_url(self):
        return reverse('accounting:detail_order', kwargs={'order_id': self.object.pk})
    
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(OrderCreate, self).dispatch(*args, **kwargs)

models.py

class OrderLine(models.Model):
    order = models.ForeignKey('Order', on_delete=models.CASCADE, related_name="has_lines", blank=True, null=True)
    order_item = models.CharField(max_length=100, verbose_name="Line", blank=True, null=True)
    tax_rate = models.ForeignKey('TaxRate', on_delete=models.CASCADE, blank=True, null=True)
    total = models.CharField(max_length=250, blank=True, null=True)
    description = models.CharField(max_length=100, blank=True, null=True)
    unit_price_excl_tax = models.DecimalField(max_digits=8,decimal_places=2, blank=True, null=True)
    quantity = models.DecimalField(max_digits=8,decimal_places=2,default=1, blank=True, null=True)

    def total(self):
        total = Decimal(str(self.unit_price * self.quantity))
        return total.quantize(Decimal('0.01'))
        
    def __unicode__(self):
        return self.description

    class Meta:
        pass


class OrderProductLine(models.Model):
    order = models.ForeignKey('Order', on_delete=models.CASCADE, related_name="has_lines", blank=True, null=True)
    baseproduct = models.ForeignKey('BaseProduct', on_delete=models.CASCADE, related_name="has_product_lines", blank=True, null=True)
    order_item = models.CharField(max_length=100, verbose_name="Product_Line", blank=True, null=True)
    tax_rate = models.ForeignKey('TaxRate', on_delete=models.CASCADE, blank=True, null=True)
    total = models.CharField(max_length=250, blank=True, null=True)
    description = models.CharField(max_length=100, blank=True, null=True)
    unit_price_excl_tax = models.DecimalField(max_digits=8,decimal_places=2, blank=True, null=True)
    quantity = models.DecimalField(max_digits=8,decimal_places=2,default=1, blank=True, null=True)

    def total(self):
        total = Decimal(str(self.unit_price * self.quantity))
        return total.quantize(Decimal('0.01'))
        
    def __unicode__(self):
        return self.description

    class Meta:
        pass

forms.py

class OrderLineForm(forms.ModelForm):

    class Meta:
        model = OrderLine
        exclude = ()

OrderLineFormSet = inlineformset_factory(
    Order, OrderLine, form=OrderLineForm,
    fields=['order_item', 'description','quantity','unit_price_excl_tax','tax_rate'], extra=1, can_delete=True
    )

class OrderProductLineForm(forms.ModelForm):

    class Meta:
        model = OrderProductLine
        exclude = ()

OrderProductLineFormSet = inlineformset_factory(
    BaseProduct, OrderProductLine, form=OrderProductLineForm,
    fields=['supplier', 'part_number','quantity','unit_price_excl_tax','tax_rate'], extra=1, can_delete=True
    )
Steve
  • 31
  • 12
  • Why do you need 2 forms? You should not be handling 2 forms in a single view. Either you can have two separate forms that have different submit URLs and submit different data or you can combine the 2 forms into a single form that submits all data at once – alexbhandari Jun 12 '21 at 14:25

1 Answers1

0

To fix the issue with your view you can combine the two get_context_data methods, but I am not sure if this will fix all the issues you are having

    def get_context_data(self, **kwargs):
        data = super(OrderCreate, self).get_context_data(**kwargs)
        if self.request.POST:
            data['lines'] = OrderLineFormSet(self.request.POST)
            data['productlines'] = OrderProductLineFormSet(self.request.POST)
        else:
            data['lines'] = OrderLineFormSet()
            data['productlines'] = OrderProductLineFormSet()
        return data

More generally, it looks like you are trying to edit two models at the same time utilizing the ModelForm class. The best way to do this is to have two model forms submit through the same form rather than utilizing formsets. There is a good example of this here taken from this SO post. You should be able to find more details on this implementation in the two links.

alexbhandari
  • 1,310
  • 12
  • 21