tags:

views:

685

answers:

3

I want to have a model with calculated fields that I can apply sorting on. For example, let's say that I have the following model:

class Foo(models.Model):
    A = models.IntegerField(..)
    B = models.IntegerField(..)
    C = models.ForeignKey(..)

I want to have a D and an E field that are calculated by the following formulas:

  1. D = A - B
  2. E = A - X (where X is a field of the relevant record of model C)

Implementing this would be trivial if I didn't need to apply sorting; I would just add properties to the model class. However, I need ordering by these fields.

A solution is to fetch all records into memory and do the sorting there, which I conceive a last resort (it will break things regarding pagination).

Is there a way to achieve what I'm trying? Any guidance is appreciated.

EDIT: Denormalization is a no-go. The value of field X changes very frequently and a lot of Foo records are related to one record of model C. An update of X will require thousands of updates of E.

+3  A: 

I would take a look at the extra method on Queryset and specify the order_by parameter.

John Paulett
Unless D and E are materialized in the database, I don't see how the extra(order_by) is going to help.
celopes
@celopes sql's order by works with on-the-fly calculated fields, therefore extra method is suitable here.
shanyu
A: 

I haven't presently got a Django install running, but I think what you're asking is how to do a custom save, such that D and E are automatically generated. I don't know what your ForeignKey's return on unicode is, so I'm assuming it's not a string and assigning "valueName" as token vlaue for the integer you want to usage.

Anyway, it should go a bit like this:

class Foo(models.Model):
    A = models.IntegerField(..)
    B = models.IntegerField(..)
    C = models.ForeignKey(..)
    D = models.IntegerField(..)
    E = models.IntegerField(..)
    def save(self):
        self.D = self.A - self.B
        self.E = self.A - self.C.valueName
        super(Foo, self).save()

Anything prior to the last line of that (super()) will be PRE save, anything after is POST. That's really the most important point there.

Swixel
This is OK if shanyu doesn't mind materializing the computed fields in the database.
celopes
Please see my edit, denormalization is not an option. Thanks for the help.
shanyu
+1  A: 

If you would not mind some logic duplicaton, then the following will work:

Foo.objects.extra(select={ 'd_field' : 'A - B' }).extra(order_by=['d_field'])
Dmitry Risenberg