simple multiplication in models

696 views
Skip to first unread message

Chris Amico

unread,
Aug 25, 2008, 5:22:34 PM8/25/08
to Django users
This seems like it should be simple, but I'm getting errors.

I have a model tracking individual purchases. User inputs an item cost
and quantity; I want the total cost calculated. Here are the relevant
parts:

class Item(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField(default=1)
total_cost = models.DecimalField(max_digits=12,
decimal_places=2,
blank=True,
null=True,
editable=False)

def total_cost(self):
total = (self.price * self.quantity)
return total_cost(total)

def save(self):
self.total_cost = self.total_cost()
super(Item, self).save()

The error I get is:

NameError at /admin/spending/item/add/
global name 'total_cost' is not defined

Not sure what that means. Thoughts? Am I missing an exceedingly
obvious way to do this?

Karen Tracey

unread,
Aug 25, 2008, 6:24:28 PM8/25/08
to django...@googlegroups.com

You're making my brain explode trying to use the single name 'total_cost' for both a model field and a method.  I'd name the method that computes the values of the 'total_cost' field something else, like 'compute_total_cost'.  Then, within it, be very careful to preface all references to model field values with 'self.'.  The error message you are getting usually happens when you forget a 'self.' in front of a reference to a model field.

Karen


nicksergeant

unread,
Aug 25, 2008, 6:45:04 PM8/25/08
to Django users
Should 'total_cost' simply:

return total

instead of:

return total_cost(total)

Chris Amico

unread,
Aug 25, 2008, 6:58:45 PM8/25/08
to Django users
Changed the variable names to make it a little more readable. Method
now returns "amount" (formerly "total"):

Class Item(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField(default=1)
total_cost = models.DecimalField(max_digits=12,
decimal_places=2,
blank=True,
null=True,
editable=False)

def calc_total(self):
amount = (self.price * self.quantity)
return amount

def save(self):
self.total_cost = self.calc_total()
super(Item, self).save()


Now I get a different error when I try to save an Item:

OperationalError at /admin/spending/item/add/
(1054, "Unknown column 'total_cost' in 'field list'")

Malcolm Tredinnick

unread,
Aug 25, 2008, 7:14:00 PM8/25/08
to django...@googlegroups.com

On Mon, 2008-08-25 at 15:58 -0700, Chris Amico wrote:
> Changed the variable names to make it a little more readable. Method
> now returns "amount" (formerly "total"):
>
> Class Item(models.Model):
> name = models.CharField(max_length=100)
> price = models.DecimalField(max_digits=9, decimal_places=2)
> quantity = models.IntegerField(default=1)
> total_cost = models.DecimalField(max_digits=12,
> decimal_places=2,
> blank=True,
> null=True,
> editable=False)
>
> def calc_total(self):
> amount = (self.price * self.quantity)
> return amount
>
> def save(self):
> self.total_cost = self.calc_total()
> super(Item, self).save()
>
>
> Now I get a different error when I try to save an Item:
>
> OperationalError at /admin/spending/item/add/
> (1054, "Unknown column 'total_cost' in 'field list'")
>

Your earlier model had a bug -- two things called total_cost. The second
thing with that name (the method) completely hid the first thing. It
wasn't just something that gave Karen a headache; it was a real bug. So
when you ran "syncdb", there was no field called total_cost because the
"total_cost" attribute was a method. Thus, you have added a new field to
the model now that you have removed the method.

You'll either have to update the database table manually, or drop and
recreate that model's table (look at the sqlreset command for
django-admin.py).

Regards,
Malcolm


Chris Amico

unread,
Aug 25, 2008, 7:29:08 PM8/25/08
to Django users
Awesome. That worked. Thanks all.


On Aug 25, 4:14 pm, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:

Fabio Natali

unread,
Aug 26, 2008, 2:45:45 AM8/26/08
to django...@googlegroups.com
Chris Amico wrote:
[...]

> Awesome. That worked. Thanks all.

> > > Class Item(models.Model):


> > >     name = models.CharField(max_length=100)
> > >     price = models.DecimalField(max_digits=9, decimal_places=2)
> > >     quantity = models.IntegerField(default=1)
> > >     total_cost = models.DecimalField(max_digits=12,
> > >                                      decimal_places=2,
> > >                                      blank=True,
> > >                                      null=True,
> > >                                      editable=False)
> >
> > >     def calc_total(self):
> > >         amount = (self.price * self.quantity)
> > >         return amount
> >
> > >     def save(self):
> > >         self.total_cost = self.calc_total()
> > >         super(Item, self).save()

I am very happy to hear that everything's working now.

I am a bit confused though: why not just dropping the total_cost field
from the table? You can retrieve that value on the fly in your views,
no need to have it "hardcoded" in your db. (It's a simple
multiplication, I guess you won't have performance issue for this
change.)

I'd appreciate if anyone can shed some light on this and point out any
drawbacks I can't see.

All the best, Fabio.

--
Fabio Natali

akaihola

unread,
Sep 1, 2008, 7:21:31 AM9/1/08
to Django users
Fabio,

The only reasons I can think of for this kind of denormalization of
the database are performance (as you mentioned, unsignificant benefit
here) or using the resulting value in a filter condition.

The latter can be better worked around by mixing raw SQL with Django's
ORM expressions (with the extra= keyword).
Reply all
Reply to author
Forward
0 new messages