znawca
znawca

Reputation: 279

How to count value of objects?

I've created shop. I have two models:

My models:

class Product(models.Model):
    name = models.CharField(verbose_name="name", max_length=40)
    cost = models.FloatField(verbose_name="price")

    def __unicode__(self):
        return self.name

class Shop(models.Model):
    product = models.ManyToManyField(Product)
    name = models.CharField(verbose_name="Nazwa", max_length=40)
    budget = models.FloatField(verbose_name="kwota")

    def __unicode__(self):
        return self.name

I created template and now I have name of shop and products with their price:

enter image description here

How can I count this prices? For example like on this picture i choose products which count total = 17. Should I create something in view and next put it into template or write it only in template?

Now i have something like:

{% for p in shop.product.all %}
        {{p.cost}} 
{% endfor %}

But what next? It only show me this values, but how to do math on this? I have no idea.

My view:

def shop_detail(request, pk):
    shop = get_object_or_404(Shop, pk=pk)
    return render(request, 'shopbudget/shop_detail.html', {'shop': shop})

Now should i create what? I created something like:

def sum_of_count(request):
    total = 0
    for cost in shop.product.all:
        total = total + cost
        return total

Upvotes: 1

Views: 146

Answers (3)

Willemoes
Willemoes

Reputation: 6367

You should add a function to your Shop model, something like:

def count_cost(self):
    products = self.product.all()
    return sum(p.cost for p in products)

And finally in your template:

{{ shop.count_cost }}

As Martin suggested the calculation should be made at database level, to get a performance boost, i would recommend this instead:

from django.db.models import Sum

def count_cost(self):
    cost_sum = self.product.all().aggregate(total=Sum('cost'))
    return cost_sum['total']

Upvotes: 2

Gocht
Gocht

Reputation: 10256

Try this:

from django.db.models import Sum

Shop.objects.filter(pk=pk).aggregate(sum_of_count=Sum('product__cost'))

That should return the sum of every product's cost in Shop object.

Upvotes: 1

Martin Alderete
Martin Alderete

Reputation: 736

The approach described by @willemoes its ok and works, my only concern is to do the calculation in python instead of at database level (performance boost). I recommend you to do the calculation at db level, in your model class (Shop) you could add the following.

from django.db.models import Sum

def calculate_cost(self, default=0.0):
    cost = Product.objects.filter(shop__id=shop_pk).aggregate(total=Sum('cost'))
    return cost['total'] or default

That code should not be expensive but if it start to take some time to return you could "cache" that calculation using the "django cache" or "@cached_property". Using the django's cache framework.

def total_cost(self, default=0.0, expire=300):
    key = "pcost_%s" % self.pk
    cost = cache.get(key)
    if cost:  # cache found!
        return cost

    cost = Product.objects.filter(shop__id=shop_pk).aggregate(total=Sum('cost'))
    value = cost['total'] or default
    cache.set(key, value, expire)  #after expire seconds will be deleted
    return value

Using @cached_property

from django.utils.functional import cached_property

@cached_property
def total_cost(self):
    cost = Product.objects.filter(shop__id=shop_pk).aggregate(total=Sum('cost'))
    return cost['total'] or 0.0

The @cached_property uses memoization. its a normal python's property. if you want to invalidate the "cache" to force a re-calculation you have to do:

# see the @cached_property docs for more info
del your_model_instance.total_cost

Hope it helps you!

Upvotes: 2

Related Questions