Total and subtotal in Django template using lists

Asked

Viewed 851 times

2

How do I calculate the subtotal and total per store (store) in the view to play the results in the template? In this case I’m using lists.

def quotation_list(request):
    stores = list(Store.objects.all())
    products = list(Product.objects.all())
    # indice
    index_store = {store.id: index for index, store in enumerate(stores)}
    index_product = {product.id: index for index,
                     product in enumerate(products)}
    # dados para o template
    cabecalho = ["Lojas"] + [store.store for store in stores]
    linhas = [([product.product] + [None for store in stores])
              for product in products]

    for pev in Quotation.objects.all():
        total = pev.price * pev.quantity
        linhas[index_product[pev.product_id]][index_store[pev.store_id] +
                                              1] = (pev.price, pev.quantity, total, pev.store, pev.product)

    # retorna o menor preço de cada produto
    # a quantidade, total e loja também estão nesta lista
    mais_barato = []
    for linha in linhas:
        mais_barato.append(min(linha[1:]))

    # destaca os menores preços no template
    mb = 0
    if request.GET.get('mais_barato', False):
        mb = 1

    # mostra somente os menores preços
    smb = 0
    if request.GET.get('somente_mais_barato', False):
        smb = 1

Notice that here I order the list by shop.

    # mostra os produtos mais baratos, a quantidade e o total
    bqt = 0
    if request.GET.get('quantidade_e_total', False):
        mais_barato = sorted(mais_barato,
                             key=lambda store: str(store[3]))  # sort by store
        bqt = 1

    context = {
        'cabecalho': cabecalho,
        'linhas_mais_barato': zip(linhas, mais_barato),
        'mb': mb,
        'smb': smb,
        'bqt': bqt,
    }
    return render(request, 'core/quotation_list.html', context)

I tried something similar to this here

https://stackoverflow.com/a/22366344/802542

but it didn’t work out.

  • @mgibsonbr goes who is your Marcelo Gibson. :)

  • I don’t understand your ordering. Are you ordering by shop, simply? Like, in alphabetical order or something? If that’s the case, it would be easier to do it right at the beginning, i.e. stores = list(Store.objects.all().order_by("store")). Now, if what you want is to reorder the columns so that the stores with the cheapest total appear in front, this is a little bit more complicated (because you would need to maintain cabecalho and linhas consistent during this ordering).

1 answer

2


I did not understand whether the subtotal refers to the rows, columns, or both, so I will reply to "both" (although in your particular case this makes no sense).

To calculate the subtotal of the row, add one more item in each row. To calculate the subtotal of the column, add one more row. During filling (when you iterate over the relationship table), you update the values of the subtotals in one and the other case:

cabecalho = ["Lojas"] + [store.store for store in stores] + ["Subtotal"]
linhas= [([product.product] + [None for store in stores] + [(0,0,0,None,product.product)])
            for product in products
        ] +
        [["Subtotal"] + [(0,0,0,store.store,None) for store in stores] + [(0,0,0,None,None)]]

def soma_tuplas(a, b):
    return (a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3], a[4])

for pev in Quotation.objects.all():
    total = pev.price * pev.quantity

    i0 = index_product[pev.product_id]
    i1 = index_store[pev.store_id] + 1
    valor = (pev.price, pev.quantity, total, pev.store, pev.product)

    linhas[i0][i1] = valor

    # Subtotal da linha (se fizer sentido no seu caso)
    linhas[i0][len(stores)+1] = soma_tuplas(linhas[i0][len(stores)+1], valor)

    # Subtotal da coluna (se fizer sentido no seu caso)
    linhas[len(produtos)][i1] = soma_tuplas(linhas[len(produtos)][i1], valor)

    # Total da tabela (se fizer sentido no seu caso)
    linhas[len(produtos)][len(stores)+1] = 
        soma_tuplas(linhas[len(produtos)][len(stores)+1], valor)

If I understand what you are modeling, only the subtotals per column make sense in your case, and the value that will interest you in the end is the sum of the totals (i.e. sum of the quantities times price of all products in a store) - ie, the index 2 tuple. But I gave a full answer if anyone in the future finds this question useful.

Detail: if you do it this way, you don’t have to touch it mais_barato, because it will calculate the subtotals minimum as well. :)


Addendum: In the your previous question I had suggested using zip to unite linhas and mais_barato - which is a quick solution to the case of iterate over two lists at the same time. But if you are going to sort one or the other, it is important to keep the elements together, otherwise the values of each row will not correspond to the values of the minima:

linhas_mais_barato = zip(linhas, mais_barato) # Primeiro faz o zip, depois ordena

# mostra os produtos mais baratos, a quantidade e o total
bqt = 0
if request.GET.get('quantidade_e_total', False):
    linhas_mais_barato = sorted(linhas_mais_barato,
                                key=lambda store: str(store[1][3]))  # sort by store
    bqt = 1

context = {
    'cabecalho': cabecalho,
    'linhas_mais_barato': linhas_mais_barato,

(I don’t know if this is right, or if what you really wanted was to sort by column and not by line, but the important thing is to maintain linhas and mais_barato consistent with each other)

  • Perfect!!! The subtotal was only per line (per store ('store')). But your reply was quite complete. And the addendum was also considered. Thanks so much for the help.

  • I left a image to share with everyone the expected, and achieved result. Thank you.

  • Just one more thing, in my case, I don’t want the subtotal of the line. linhas[i0][len(stores) + 1] how do I take it out? I tried to identify it by its index to take it out only in the template but it didn’t work. At the moment I’m using {% for linha, mais_barato in linhas_mais_barato %} - {% for item in linha %} - <td>{{ item }}</td>

  • I had to edit the question from the addendum, because it was not well prepared, and did not return the expected result. I mean, your solution was useful, but I could not return what I expected from the reprint of my question. Please read from the addendum. Thanks.

  • @Regisdasilva This changes the question a lot, in one case the table is Product x Store, in the other I see that the table is products and shop is a column. I can’t adapt the answer, so I suggest you open up another question to that. Even I would use a different strategy for this case, one that does the heavy lifting on the BD itself and not on the view. Now I’m out of time, later I’ll explain better.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.