Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django ORM equivalent for this SQL..calculated field derived from related table

I have the following model structure below:

class Master(models.Model): 
    name = models.CharField(max_length=50)          
    mounting_height = models.DecimalField(max_digits=10,decimal_places=2)   

class MLog(models.Model):                  
    date = models.DateField(db_index=True)
    time = models.TimeField(db_index=True)      
    sensor_reading = models.IntegerField()      
    m_master = models.ForeignKey(Master)

The goal is to produce a queryset that returns all the fields from MLog plus a calculated field (item_height) based on the related data in Master

using Django's raw sql:

querySet = MLog.objects.raw('''
    SELECT a.id,
           date,
           time,
           sensor_reading,
           mounting_height,
          (sensor_reading - mounting_height) as item_height 
    FROM db_mlog a JOIN db_master b 
                     ON a.m_master_id = b.id
''')                                

How do I code this using Django's ORM?

like image 716
gtujan Avatar asked Apr 20 '26 02:04

gtujan


1 Answers

I can think of two ways to go about this without relying on raw(). The first is pretty much the same as what @tylerl suggested. Something like this:

class Master(models.Model):
    name = models.CharField(max_length=50)
    mounting_height = models.DecimalField(max_digits=10,decimal_places=2)

class MLog(models.Model):
    date = models.DateField(db_index=True)
    time = models.TimeField(db_index=True)
    sensor_reading = models.IntegerField()
    m_master = models.ForeignKey(Master)

    def _get_item_height(self):
        return self.sensor_reading - self.m_master.mounting_height
    item_height = property(_get_item_height)

In this case I am defining a custom (derived) property for MLog called item_height. This property is calculated as the difference of the sensor_reading of an instance and the mounting_height of its related master instance. More on property here.

You can then do something like this:

In [4]: q = MLog.objects.all()

In [5]: q[0]
Out[5]: <MLog: 2010-09-11 8>

In [6]: q[0].item_height
Out[6]: Decimal('-2.00')

The second way to do this is to use the extra() method and have the database do the calculation for you.

In [14]: q = MLog.objects.select_related().extra(select = 
          {'item_height': 'sensor_reading - mounting_height'})

In [16]: q[0]
Out[16]: <MLog: 2010-09-11 8>

In [17]: q[0].item_height
Out[17]: Decimal('-2.00')

You'll note the use of select_related(). Without this the Master table will not be joined with the query and you will get an error.

like image 161
Manoj Govindan Avatar answered Apr 21 '26 16:04

Manoj Govindan