AboJo TheGreat
AboJo TheGreat

Reputation: 71

Django how to build an invoice that gives the user the ability to add multiple items?

I am trying to build a e-shop using django 2.0, but I have a 'logic' question not a syntax one, how to build an invoice that can accept many Items? In other words I have a model for the invoice item that is connected To a product model but I do not know how to build an invoice view that That gives the user the ability to add multiple items to this invoice How to do so knowing that I use CBV for the views? Thanks in advance

Upvotes: 1

Views: 8188

Answers (1)

DisneylandSC
DisneylandSC

Reputation: 966

What you are looking for are formsets. Basically you create a normal product form, and then apply formset_factory to it to create a formset, which is an itterable object. https://docs.djangoproject.com/en/2.0/topics/forms/formsets/

Also there are a few variations, such as the model formset which accepts queries to populate the forms. I'm not sure about your project but it's worth looking at these different versions.

Edit:

It's hard for me to guess exactly what you need since I can't see the application and its views as you have them in mind exactly. Below I have created a simple example that allows you to create a new invoice with 2 orderlines attached to it. If you want to edit existing invoices you will have to add an instance, which for forms initialize, e.g.

order_formset = OrderFormSet(request.POST, initial=...)

if you want to add more than 2 orderlines, you will have to copy the forms in your formset inside the template. This can be achieved with java / jquery, there are plenty examples out there on how to do this. I suggest looking around for one that fits your needs. By and large the jquery code will copy the first / last form and paste it into your template, clear all the values of the new copy, and update the id's of the inputs in the form. See e.g. Dynamically adding a form to a Django formset with Ajax

Example:

models.py

class Invoice(models.Model):
    ...

class Order(models.Model):
    ...
    ordered = models.ForeignKey('Invoice', related_name = 'orderline')

forms.py:

class InvoiceForm(ModelForm):
    class Meta:
        model = Invoice
        fields = ['field_names_go_here']

class OrderForm(ModelForm):
    class Meta:
        model = Order
        fields = ['field_names_go_here']

views.py

def create_order(request):

    if request.method == 'POST':

        invoice = InvoiceForm(request.POST)
        if invoice.is_valid():
            invoice.save()

        OrderFormSet = formset_factory(OrderForm)
        order_formset = OrderFormSet(request.POST)

        if order_formset.is_valid():

            new_orders = []
            for order_form in order_formset:
                new order = Order(
                    ...
                    ordered = invoice,
                )
                new_orders.append(order)

            Order.objects.bulk_create(new_orders)

        return ...

    if request.method == 'GET':

       invoice_form = InvoiceForm()

       OrderFormSet = formset_factory(OrderForm, extra=2)
       order_formset = OrderFormSet()

       template_context = {
           'invoice_form': invoice_form,
           'order_formset': order_formset,
           }

       template = 'template_name.html'

       return render(request, template, template_context)

template

<form enctype="multipart/form-data" action="{% url 'some_url_name' %}" method="post">
    {% csrf_token %}

        {{ order_formset.management_form }}

        {{ invoice_form }}

        {% for order_form in order_formset %}
             {{ order_form }}
        {% endfor %}

        <input type="submit" value="submit" />
</form>

Edit2: It occured to me that you might have preset items you can order. In which case all you need to do is set up a many to many relationship, generate a list of order objects, display them in your template and add a form/button for each one that adds them to some invoice. This can be achieved by adding a hidden input containing the pk of the Order object. Then you won't even need to bother with formsets.

Upvotes: 3

Related Questions