Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting local variables in a django model

I have the following model:

class MeasurementParameter(models.Model):    
    tolerance = models.FloatField()
    set_value = models.FloatField()

    tol_low = None
    tol_high = None

    def tolerance_band(self):

        tol = self.set_value * self.tolerance/100

        self.tol_high = self.set_value + tol
        self.tol_low = self.set_value - tol

        print self.tol_low

        return self.tol_high, self.tol_low

I wish to set the calculated local variables tol_low and tol_high using the tolerance_band method.

The model is has a ManyToMany relationship with another model called Product.

class Product(models.Model):
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=1000)
    parameters = models.ManyToManyField(MeasurementParameter, related_name='measurement')

    def calc_all_tol_bands(self):

        for parameter in self.parameters.all():
            hi, lo = parameter.tolerance_band()

    def __str__(self):
        return self.name

So in my view I attempt to calculate all tolerance bands by:

product.calc_all_tol_bands()

However if I try and get the local variables:

product.parameters.all()[0].tol_low

I get None all the time.

What do I need to do to be able to set calculated values in the MeasurementParameter model?

John.

like image 442
user1988235 Avatar asked Dec 03 '25 15:12

user1988235


1 Answers

This is expected behavior. When you evaluate

product.parameters.all()[0]

this means you make a database fetch. So Django will fetch the first of these parameters. Since the tol_low and tol_high are not persistent (not stored in the database), this means that it will fallback on the class attribute, which is None.

The calculations here, are rather simple, so I propose that you convert these to properties [Python-doc]:

class MeasurementParameter(models.Model):    
    tolerance = models.FloatField()
    set_value = models.FloatField()

    @property
    def tol_low(self):
        return self.set_value * (100-self.tolerance)/100

    @property
    def tol_high(self):
        return self.set_value * (100+self.tolerance)/100

    def tolerance_band(self):
        return self.tol_high, self.tol_low

Here we thus will evaluate the property when necessary. This is more robust: if you change the tolerance of an object, or the set_value, then the tol_low and tol_high will be different for that object. So there is no complex code in place to update the value for relevant updates. The calc_all_bands is not necessary either, since calculations are simply done when requested.

Note that you can not use properties in Django ORM filters, etc. In that case, you can encode the property as a query expression and annotate the queryset with these.

like image 140
Willem Van Onsem Avatar answered Dec 05 '25 05:12

Willem Van Onsem