Return function as a field on django model

12,310

Solution 1

One way to do this is to create a Manager and defines a function with the raw SQL query. Create the model object attaching the new calculated field referencing to the model class with self.model

Class MyModel(models.Model):
    fieldA = models.FloatField() 
    fieldB = models.FloatField()
    objects = MyModelManager() #Binds the manager to the model


Class MyModelManager(models.Manager):
    def ReturnDelta(self):
        from django.db import connection
        cursor = connection.cursor()
        cursor.execute = """SELECT fieldA, fieldB, fieldA-fieldB as delta
                            from MyModel_table"""
        result_list = []

        for row in cursor.fetchall():
            p = self.model(fieldA=row[0], fieldB[1]) #access the model associated w/ the manager
            p.delta = row[2] #adds the new field to the queryset
            result_list.append(p)
        return result_list 

Almost a copy-paste from Django Documentation Then we can use the manager function in the extra-context dictionary of the generic view. and do algebraic operations without hitting the database unnecessarily.

A better solution is create your own QuerySet class with a custom Manager. This allows to chain filters and return any computed value as a QuerySet attribute. Is taken almost directly from this snippet

class CustomManager(models.Manager):
    '''
       An general purpose manager which allows to filter a queryset
       and chain filters
       "Constructor": CustomManager(CustomQuerySetClass)
    '''

    def __init__(self, qs_class=models.query.QuerySet):
        super(CustomManager,self).__init__()
        self.queryset_class = qs_class

    def get_query_set(self):
        return self.queryset_class(self.model)

    def __getattr__(self, attr, *args):
        try:
            return getattr(self.__class__, attr, *args)
        except AttributeError:
            return getattr(self.get_query_set(), attr, *args)


class MyModelQuerySet(models.query.QuerySet):
    '''
       A very specific queryset designed to return a computed value (this
       time a sum of all rows values)
    '''
    def filter(self, *args, **kwargs):
        qs = super(MyModelQuerySet, self).filter(*args,**kwargs)
        sum=0
        for row in qs:
            sum += row.delta    #use a callback to prevent from caching
        setattr(qs, 'sum',sum)
        return qs 


class MyModel(models.Model):
    objects = CustomManager()    #Binds the manager to the model
    fieldA = models.FloatField() 
    fieldB = models.FloatField()
    def delta(self):
        return self.fieldA - self.fieldB

Solution 2

Did you try that?

Class MyModel(models.Model):
    fieldA = models.FloatField()
    fieldB = models.FloatField()

def __delta(self):
    return self.fieldA - self.fieldB

delta = property(__delta)

Solution 3

For what you want just add a new method to the model and do the aggregation on the DB then just calculate the difference between the two sums. Like this:

class MyModel(models.Model):
    fielda = ...
    fieldb = ...
    def delta(self):
        return self.fielda-fieldb
    def aggregate_delta(self):
        return MyModel.objects.aggregate(Sum('fielda')) - MyModel.objects.aggregate(Sum('fieldb'))
Share:
12,310

Related videos on Youtube

marcoslhc
Author by

marcoslhc

Web Designer Twitter Github My Page

Updated on June 04, 2022

Comments

  • marcoslhc
    marcoslhc almost 2 years

    I have a model which have a function to calculate the difference between two fields Example:

    Class MyModel(models.Model):
        fieldA = models.FloatField()
        fieldB = models.FloatField()
    
        def delta(self):
            return self.fieldA - self.fieldB
    

    Id like to use this model in a GenericView. I can use the function delta as an extraContext but also id like to include the sum of all Delta results in the template, in this case I have to make an aggregate but again since delta is not a DB Field nor a Model Field I cant use it in an aggregate function.

    How can accomplish this?

    • marcoslhc
      marcoslhc about 14 years
      All answers are right, they all results in a difference of two float fields but I ended using the @daniel solution it worked OK.
  • dwurf
    dwurf about 11 years
    Not a lambda. The sum function is operating on a list comprehension
  • E.Serra
    E.Serra over 3 years
    how does this answer or help with the question?