JayB
JayB

Reputation: 77

How to display dynamic django data from views in html?

This seems like a simple problem but i just can't get it right.

I have three django models: ProjectName,ProjectBudget and ProjectActualCost.

ProjectName stores the project name (ie Project X).
ProjectBudget stores the project_name(as a foreign key(FK)),budget_name and the total_budget and date for each budget. ie (Project X, Hotel,600),(Project X, Rental,500). '600' refers to $600. ProjectActualCost stores each cost as it is incurred(itemized costs) (ie Hotel: 100, Rental: 50, Food:100) and its date. So it stores the 'project_name'(as FK),'budget_name'(as FK),actual_used and date. ie (Project X,Hotel,100,10/03/2019),(Project X,Hotel,100,10/06/2019), (Project X,Rental,50,04/10/2019)

I'm trying to render the 'Project Name', 'Budget' and 'Actual Cost' in an html table. 'Project Name' and 'Budget' are rendering correctly,but 'Actual Cost' only displays one amount for all cost item.

Models:

class ProjectName(models.Model):
   project_name = models.CharField('Name',max_length = 15,blank = False)

  def __str__(self):
    return self.project_name 

class ProjectBudget(models.Model):
   project_name = models.ForeignKey(ProjectName,on_delete = models.CASCADE, 
          null = True)
   budget_name = models.CharField('Budget Name'max_length = 50
   total_budget =  models.DecimalField('Total Budget',max_digits = 9,decimal_places=2)

   def __str__(self):
    return self.budget_name 

class ProjectActualCost(models.Model):
   project_name = models.ForeignKey(ProjectName,on_delete = models.CASCADE, null = True)
   cost_description = models.ForeignKey(ProjectBudget,on_delete = models.CASCADE,null=True)
   actual_used = models.DecimalField('Actual Used',max_digits = 15,decimal_places = 2)

Views:

def budgetview(request,project_id):
    budget_items = ProjectBudget.objects.filter(project_name_id =project_id)

    for budget in budget_items:
         budget_id = budget.id
         actual_cost = 
ProjectActualCost.objects.filter(cost_description_id= 
budget_id).aggregate(Sum('actual_used')).get('actual_used__sum') or 0

         print(budget.cost_description,":",actual_cost)#To test the output on StatReloader

    budget_template = "budget_view.html"

    context = {"budget_items":budget_items ,"actual_cost":actual_cost}

    return render(request,budget_template,context)

budget_view.html:

<table>
 <thead>
   <tr>
   <th>Project Name</th>
   <th>Budget Name</th>
   <th>Total Budget</th>
   <th>Total Used</th>
   </tr>

 <tbody>
    {% for item in budget_items%}
   <tr> 
        <td>{{item.project_name}}</td>
        <td>{{item.cost_description}}</td>
        <td>{{item.total_budget}}</td>
        <td>{{actual_cost}}</td>
   </tr>
   {%endfor%}

 </tbody>
 </table>

I'm expecting to see:

Project Name|Budget Name | Total Budget| Total Used
 Project X     Hotel        600            200
 Project X     Rental       500            50

StratReloader displays:

Hotel:200
Rental:50

But when i render budget_view.html, I get:

 Project Name|Budget Name | Total Budget| Total Used
 Project X   |  Hotel     |   600       |    50
 Project X   |  Rental    |   500       |    50          

I think the issue is that budget_id is not dynamic when the for loop runs in html.

Upvotes: 0

Views: 683

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 600041

You've only got one actual_cost variable, which you repeatedly overwrite throughout the loop.

But you don't want to use a loop, and you don't want to use aggregate. You want to use annotate, which asks the database for the aggregate value for every item in the queryset at once.

budget_items = ProjectBudget.objects.filter(project_name_id=project_id).annotate(
     actual_cost=Sum('projectactualcost__actual_used')
)
budget_template = "budget_view.html"
context = {"budget_items": budget_items}
return render(request, budget_template, context)

and in the template:

{% for item in budget_items %}
<tr> 
    <td>{{ item.project_name }}</td>
    <td>{{ item.cost_description }}</td>
    <td>{{ item.total_budget }}</td>
    <td>{{ item.actual_cost }}</td>
</tr>
{% endfor %}

Upvotes: 1

Related Questions