5

Possible Duplicate:
django - ordering queryset by a calculated field

How can i use order_by like order_by('field1'*'field2') For example i have items with price listed in different currencies, so to order items - i have to make currency conversion.

class Currency(models.Model):
    code    = models.CharField(max_length=3, primary_key=True) 
    rateToUSD   = models.DecimalField(max_digits=20,decimal_places=10)

class Item(models.Model):
    priceRT     = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    cur     = models.ForeignKey(Currency)

I would like to have something like:

Item.objects.all().order_by(F('priceRT')*F('cur__rateToUSD'))

But unfortunately it doesnt work, i also faild with annotate. How can i permorm QuerySet ordering by result of value multiplication of 2 model's fields.

Community
  • 1
  • 1
Andrew
  • 3,165
  • 4
  • 24
  • 29

2 Answers2

4

Use the extra() method. Specifically the select argument to specify the equation, and the order_by argument to do the ordering.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 1
    Thanks! in my case solution looks like - Item.objects.filter(q&qDates).extra(select={'usdPrice': 'SELECT airsale_item.priceRT/toUSD FROM airsale_currency WHERE airsale_currency.code = airsale_item.cur_id' }).order_by('usdPrice') Unfortunately i failed to use result of one extra in next one. like I.objects.extra({'xx':....}).extra({'yy': 'xx'*2}) django ( db in fact) throws 'xx' field is missing. Anyway my problem is solved, thanks – Andrew Jun 17 '10 at 13:25
2

Boring way: add an extra field to your Item class called priceUSD, and populate it with an overriden save method. means you don't have the burden of running calculations on every single query - just on every single update. so whether that's good or not will depend on whether you tend to write more or read more (given your question, maybe it's read?)

something like this:

class Item(models.Model):
    priceRT     = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    cur     = models.ForeignKey(Currency)
    priceUSD = models.DecimalField(max_digits=15, decimal_places=2, default=0)

    def save(self,*args,**kwargs)
        self.priceUSD = self.priceRT * self.cur.rateToUSD
        super(Model,self).save(*args,**kwargs)

In my django stuff, whenever I've tried to implement clever calculated fields without storing them in the database, I've usually found it comes with too many disadvantages to be usefult (eg can't query on them, can't sort on them). so storing a proper field in the DB with a custom save method is how I've usually done it, and it works ok. you'll want a bit more error-checking and stuff tho.

hwjp
  • 15,359
  • 7
  • 71
  • 70
  • I think the main advantage of this approach is that you can add an index for your calculated field, making your read query much faster on big tables. – Ivan Virabyan Oct 08 '13 at 06:05