Reputation:
So I'm creating an expense sheet app in Django and I'm trying to get the total sum of all the expense costs, which I then want to display in my template.
My Model:
class Expense(models.Model):
PAYMENT_CHOICES = (
('Cash', 'cash'),
('Credit', 'credit'),
('Debit', 'debit')
)
date = models.DateField()
store = models.CharField(max_length=100)
price = models.DecimalField(max_digits=20, decimal_places=2)
payment_type = models.CharField(max_length=8, choices=PAYMENT_CHOICES)
category = models.CharField(max_length=100)
def __unicode__(self):
return u'%s' % (self.date)
def _price_sum(self):
return self.objects.aggregate(total_price = Sum('price'))
price_sum = property(_price_sum)
I'm trying to call 'price_sum' in my template with the tag {{ expenses.price_sum }}
my template looks like this
{% if expenses %}
<table class='table table-hover'>
<tr>
<thead>
<th>Date</th>
<th>Store</th>
<th>Price</th>
<th>Payment Type</th>
<th>Category</th>
<th></th>
<th></th>
</thead>
</tr>
{% for expense in expenses %}
<tr>
<tbody>
<td>{{expense.date}}</td>
<td>{{expense.store}}</td>
<td>${{expense.price|floatformat:"2"|intcomma}}</td>
<td>{{expense.payment_type}}</td>
<td>{{expense.category}}</td>
<td>
<form action='{{expense.id}}/' method='get'>
<input type="submit" value="Edit" class="btn">
</form></td>
<td>
<form action='{{expense.id}}/delete/' method='post'>{% csrf_token %}
<input type="submit" value="Delete" class="btn btn-danger" data-dismiss="alert">
</form></td>
</tbody>
</tr>
{% endfor %}
</table>
<h3>Table: {{ expenses.price_sum }}</h3>
{% else %}
<p>No expenses logged.</p>
{% endif %}
I'm not certain if I'm using the template tag incorrectly or if my function is wrong or what is going wrong. I know this is probably really simple but I've searched all over StackOverflow and the Django and Python docs and anything I could find and I just can't figure it out.
UPDATE: views.py
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from expenses.models import Expense
from expenses.form import ExpenseForm
def index(request,template='expenses/index.html'):
all_expenses = Expense.objects.all()
return render(request, template, {'all_expenses': all_expenses})
def new(request, template='expenses/new.html'):
if request.method == 'POST':
new_expense = ExpenseForm(request.POST)
if new_expense.is_valid() and new_expense.clean():
new_expense.save()
return HttpResponseRedirect('/expenses/')
else:
new_expense = ExpenseForm()
return render(request, template, {'new_expense':new_expense})
def edit(request, expense_id, template='expenses/edit.html'):
expense = Expense.objects.get(id=expense_id)
if request.method == 'POST':
form = ExpenseForm(request.post, instance=expense)
if form.is_valid and form.clean():
expense = form.save()
return HttpResponseRedirect('/expenses/')
else:
expense = ExpenseForm(instance=expense)
return render(request, template, {'expense':expense})
def delete(request, expense_id):
if request.method == 'POST':
expense = Expense.objects.get(id=expense_id).delete()
return HttpResponseRedirect('/expenses/')
else:
return HttpResponse(status=404)
Upvotes: 2
Views: 1736
Reputation: 599530
You don't say which of those views is the one you want to use the aggregate in (and I wonder why you've posted all of them). However, what's clear is that you never pass an Expense
object in any of them, and since price_sum
is an instance method on Expense, you will never have access to it.
But actually you don't want to do that. price_sum
should not be an instance method, as it never interacts with the instance at all. In fact, if you tried to run the method from the shell, you would see this error:
AttributeError: Manager isn't accessible via Expense instances
which should be self-explanatory.
Instead of doing this, you should define a custom manager and define a price_sum method there. Then you can acccess Expenses.objects.price_sum()
, which you can pass directly to your template in the context.
Upvotes: 0
Reputation: 369044
aggregate
return dictionary that looks like {'total_price': Decimal('1234')}
.
So, change _price_sum
as follow:
def _price_sum(self):
return self.objects.aggregate(total_price=Sum('price'))['total_price']
Or, change the template as follow:
{{ expenses.price_sum.price_sum }}
Upvotes: 2