Django Regoup - Sum in templates

Hello,

I want to get the Volume subtotal of my “Contracts” field grouped in my template.

I manage to display the total but not the subtotal.

Here are my files:

views.py

class TransportDetListView(LoginRequiredMixin,ListView):
login_url = 'admin:login'
model = TransColisCgDlaDet
template_name = "betou/transports/sciages/trans_detail.html"
paginate_by = 15

def get_queryset(self):
    queryset = TransColisCgDlaDet.objects.filter(code_trans__contains=self.kwargs['code_trans']).order_by('num_contrat','essence','epaisseur')
    lstcolis = queryset.values(
        'num_colis', 
        'essence', 
        'epaisseur', 
        'qualite', 
        'produit',
        'num_contrat',
        'code_specif',
        'code_specif_douane',
        'destinataire',
        'port_destination',
        'marque',
        'receptionnaire',
        'code_trans'
        ).annotate(volumecolis=Sum('cubage')).annotate(nb_elts=Sum('nbre_elts'))

    return lstcolis


def get_context_data(self, **kwargs):
    context = super(TransportDetListView, self).get_context_data(**kwargs)
    context['codetrans_filter'] = TransColisCgDlaDet.objects.filter(code_trans__contains=self.kwargs['code_trans']).order_by('num_contrat','essence','epaisseur')
    context['nbrecolis'] = context['codetrans_filter'].values('num_colis').order_by('num_contrat').aggregate(nbrecolis=Count('num_colis')).get('nbrecolis')
    context['totalvolume'] = context['codetrans_filter'].values('cubage').aggregate(totalvolume=Sum('cubage')).get('totalvolume')
    context['totalelts'] = context['codetrans_filter'].values('nbre_elts').aggregate(totalelts=Sum('nbre_elts')).get('totalelts')
    context['contrats'] = context['codetrans_filter'].annotate(volumecolis=Sum('cubage')).annotate(nb_elts=Sum('nbre_elts')) 
    return context

template

{% regroup contrats by num_contrat as lst_group %}

                <table class="table table-responsive table-bordered text-center">
                    {% for group_ct in lst_group %} 
                        <thead>
                            <tr>
                                <td colspan = "15" class="align-center" style="font-weight: 600">{{ group_ct.grouper }}</td>
                            </tr>
                             <tr>
                            <tr>
                                <th>N.Contrat</th>
                                <th>Essence</th>
                                <th>Epais.</th>
                                <th>N.Colis</th>
                                <th>Nb Elts</th>
                                <th>Volume</th>
                                <th>Qualité</th>
                                <th>Produit</th>
                                <th>Specif</th>
                                <th>Specif Douane</th>
                                <th>Destinataire</th>
                                <th>Destination</th>
                                <th>Marque</th>
                                <th>Réceptionnaire</th>
                                <th>Code Trans</th>
                            </tr>
                        </thead>
                        <tbody>
                        
                            
                                {% for trans in group_ct.list %}
                                    <tr>
                                        <td>{{trans.num_contrat|default_if_none:""}}</td>
                                        <td>{{trans.essence}}</td>
                                        <td>{{trans.epaisseur}}</td>
                                        <td>{{trans.num_colis}}</td>
                                        <td style="text-align: right;">{{trans.nb_elts}}</td>
                                        <td style="text-align: right;">{{trans.volumecolis}}</td>
                                        <td>{{trans.qualite|default_if_none:""}}</td>
                                        <td>{{trans.produit|default_if_none:""}}</td>
                                        <td >{{trans.num_contrat}}&nbsp{{trans.code_specif}}</td>
                                        <td>{{trans.code_specif_douane|default_if_none:""}}</td>
                                        <td>{{trans.destinataire|default_if_none:""}}</td>
                                        <td>{{trans.port_destination|default_if_none:""}}</td>
                                        <td>{{trans.marque|default_if_none:""}}</td>
                                        <td>{{trans.receptionnaire|default_if_none:""}}</td>
                                        <td><a href="{% url 'betou:transport_codetrans' trans.code_trans %}" class="link text-secondary">{{trans.code_trans}}</a> {% if not forloop.last %}{% endif %}</td>
                                    </tr>
                                {% endfor %}
                                <tr>
                                    <td colspan = "3" style="text-align: center; font-weight: 600;">TOTAUX:</td>
                                    <td style="text-align: right; font-weight: 600;">{{ group_ct.list|length }} Colis</td>
                                    <td style="text-align: right; font-weight: 600;">{{ group_ct.list.nb_elts }}</td>
                                    <td style="text-align: right; font-weight: 600;"> {{ group_ct.list.volumecolis }}</td>
                                    <td colspan = "12"></td>
                                </tr>                                                
                            {% endfor %}

                       
                                <tr>
                                    <td colspan = "3" style="text-align: center; font-weight: 600;">TOTAUX:</td>
                                    <td style="text-align: right; font-weight: 600;">{{ nbrecolis }} Colis</td>
                                    <td style="text-align: right; font-weight: 600;">{{ totalelts }}</td>
                                    <td style="text-align: right; font-weight: 600;"> {{ totalvolume }}</td>
                                    <td colspan = "12"></td>
                                </tr>
                         
                        </tbody>
                </table>

I get this result:

You see that the Parcel count is done correctly {{ group_ct.list|length }}. But for the sum I don’t have a subtotal result.

Thanks

Can you do the aggregation in the view and include that value within the context?

Otherwise you could create a custom template tag to do exactly what you need.

Thanks for your suggestion.

Finally I modify my models and add a contrats table. Then I create a relationship between my two tables with the contrats field.

This is my new views:

class StockRoulantContratsListView(LoginRequiredMixin,ListView):
    login_url = 'admin:login'
    model = I_Contrats
    template_name = "douala/stocks/sciages/stock_roulant_contrats.html"
    paginate_by = 1 # Affiche 1 Contrata par Page


    def get_queryset(self): 
        criterio1=Q(transcoliscgdladet__num_contrat__isnull=False)
        criterio2=Q(transcoliscgdladet__id_trans_colis_cg_dla_id__receptranssciages__isnull=True)
        criterio3=Q(id_trans_colis_cg_dla_id__receptranssciages__isnull=True)

        colis = TransColisCgDlaDet.objects.filter(criterio3).order_by('num_contrat','essence','epaisseur','num_colis')
        contrats = I_Contrats.objects.filter(criterio1 & criterio2)\
            .annotate(volumect=Sum('transcoliscgdladet__cubage'))\
            .annotate(nbreeltsct=Sum('transcoliscgdladet__nbre_elts'))\
            .annotate(nbrect=Count('transcoliscgdladet__num_colis'))\
                .prefetch_related(Prefetch('transcoliscgdladet_set',queryset=colis\
                        .annotate(volumecolis=Sum('cubage'))\
                        .annotate(nbeltscolis=Sum('nbre_elts'))\
                            ,to_attr='contrat_with_cubage')
        )

        return contrats

    
    def get_context_data(self, **kwargs):
        criterio3=Q(id_trans_colis_cg_dla_id__receptranssciages__isnull=True)
        criterio4=Q(receptranssciages__isnull=True)
        context = super(StockRoulantContratsListView, self).get_context_data(**kwargs)

        context['codetrans_filter'] = TransColisCgDlaDet.objects.filter(criterio3).order_by('num_contrat','essence','epaisseur','num_colis')
        context['totalvolume'] = context['codetrans_filter'].values('cubage').aggregate(totalvolume=Sum('cubage')).get('totalvolume')
        context['totalelts'] = context['codetrans_filter'].values('nbre_elts').aggregate(totalelts=Sum('nbre_elts')).get('totalelts')
        context['totalcolis'] = context['codetrans_filter'].values('num_colis').aggregate(totalcolis=Count('num_colis')).get('totalcolis')
        context['camions_filter'] = TransColisCgDla.objects.only('code_trans').filter(criterio4).order_by('code_trans')
        context['nbrecamions'] = context['camions_filter'].aggregate(nbrecamions=Count('code_trans')).get('nbrecamions')

        return context

and then my template:

<table class="table table-responsive table-bordered text-center">
                        {% for ct in i_contrats_list %}
                        
                            <thead>
                                <tr>
                                    <td colspan = "15" class="align-center" style="font-weight: 600">{{ ct.num_contrat }}</td>
                                </tr>
                                 <tr>
                                <tr>
                                    <th>N.Contrat</th>
                                    <th>Essence</th>
                                    <th>Epais.</th>
                                    <th>N.Colis</th>
                                    <th>Nb Elts</th>
                                    <th>Volume</th>
                                    <th>Qualité</th>
                                    <th>Produit</th>
                                    <th>Specif</th>
                                    <th>Specif Douane</th>
                                    <th>Destinataire</th>
                                    <th>Destination</th>
                                    <th>Marque</th>
                                    <th>Réceptionnaire</th>
                                    <th>Code Trans</th>
                                </tr>
                            </thead>
                            <tbody>

                            {% for trans in ct.contrat_with_cubage %}
                                <tr>
                                    <td>{{trans.num_contrat|default_if_none:""}}</td>
                                    <td>{{trans.essence}}</td>
                                    <td>{{trans.epaisseur}}</td>
                                    <td>{{trans.num_colis}}</td>
                                    <td style="text-align: right;">{{trans.nbeltscolis}}</td>
                                    <td style="text-align: right;">{{trans.volumecolis}}</td>
                                    <td>{{trans.qualite|default_if_none:""}}</td>
                                    <td>{{trans.produit|default_if_none:""}}</td>
                                    <td >{{trans.num_contrat}}&nbsp{{trans.code_specif}}</td>
                                    <td>{{trans.code_specif_douane|default_if_none:""}}</td>
                                    <td>{{trans.destinataire|default_if_none:""}}</td>
                                    <td>{{trans.port_destination|default_if_none:""}}</td>
                                    <td>{{trans.marque|default_if_none:""}}</td>
                                    <td>{{trans.receptionnaire|default_if_none:""}}</td>
                                    <td><a href="{% url 'betou:transport_codetrans' trans.code_trans %}" class="link text-secondary">{{trans.code_trans}}</a> {% if not forloop.last %}{% endif %}</td>
                                </tr>
                            {% endfor %}
                            <tr>
                                <td colspan = "3" style="text-align: center; font-weight: 600;">TOTAL / CONTRAT:</td>
                                <td style="text-align: right; font-weight: 600;">{{ ct.nbrect }} Colis</td>
                                <td style="text-align: right; font-weight: 600;">{{ ct.nbreeltsct }}</td>
                                <td style="text-align: right; font-weight: 600;"> {{ ct.volumect }}</td>  
                                <td colspan = "9"></td>
                            </tr> 

                        {% endfor %}

                        {% if not page_obj.has_next %}
                        <tr>
                            <td colspan = "3" style="text-align: center; font-weight: 600;">TOTAUX:</td>
                            <td style="text-align: right; font-weight: 600;">{{ totalcolis }} Colis</td>
                            <td style="text-align: right; font-weight: 600;">{{ totalelts }}</td>
                            <td style="text-align: right; font-weight: 600;"> {{ totalvolume }}</td>
                            <td colspan = "8"></td>
                            <td style="text-align: right; font-weight: 600;"> {{ nbrecamions }} Transport(s)</td>  
                        </tr>
                        {% endif %}


                        </tbody>
                    </table>

Finally the result:

My doubt is always It’s that possible the same result using only one table without relationship?

It may be possible to do it that way - but that’s a poor database design.

In general, you’ll find that a good design works toward more tables rather than fewer. The goal is to reduce unnecessary redundancy of data elements and enhance the enforcement of the internal integrity of your data.

A relational database engine exists to optimize these join operations between tables. Otherwise, we might as well go back to using flat files for data storage.

You’ll often read about “normalization” of table structures, with most references talking about designing your database in “Third-normal form”. That is your theoretical objective for how your tables should be designed. (Note, it’s not always practical to end up there, which is why I refer to it as an “objective” and not something “required”. But I always work from the approach of designing to 3rd normal form, then denormalizing when necessary. There are degrees of normalization beyond the third, but you don’t tend to see them being used outside of some specific situations.)

1 Like

Thanks for your reply.

I know the table layout is not good. But that table is like this because it is a table created from a query on another remote server from an MS-Access application via ODBC to MySQL.
Then that new table is replicated to this server where my project Django app is installed.
I imagine they did that table layout because they didn’t think to integrate the data into another application. Normally that table was exported and used in MS-Excel.

I am already changing the query in MS-Access so that they export the data in different tables and then replicate it on my server.