返回函数作为django模型上的字段

时间:2010-02-05 15:22:00

标签: django django-models django-templates

我有一个模型,它具有计算两个字段之间差异的功能 例如:

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

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

我想在GenericView中使用此模型。我可以使用函数delta作为extraContext,但也喜欢在模板中包含所有Delta结果的总和,在这种情况下我必须再次进行聚合,因为delta不是DB Field,也不是模型字段我不能使用它在一个聚合函数中。

如何做到这一点?

5 个答案:

答案 0 :(得分:3)

你试过吗?

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

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

delta = property(__delta)

答案 1 :(得分:2)

执行此操作的一种方法是创建Manager并使用原始SQL查询定义函数。使用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 

几乎来自Django Documentation的复制粘贴 然后我们可以在泛型视图的extra-context字典中使用manager函数。并且在不必要地访问数据库的情况下进行代数运算。

更好的解决方案是使用自定义Manager创建自己的QuerySet类。这允许链接过滤器并将任何计算值作为QuerySet属性返回。几乎直接来自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

答案 2 :(得分:1)

这怎么可行?聚合在数据库中完成,但您的计算显然是在Python中。

你也必须在Python中做总结:

aggregated_delta = sum([m.delta() for m in MyModel.objects.all()])

答案 3 :(得分:1)

好的,首先你的代码应该有自己,因为你想要计算每一行的增量。

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

关于您的问题,无法从django函数聚合。有三种方法可以实现您的目标:

  1. 在Python中进行聚合。缺点:从数据库下载所有数据。

  2. 绕过Django ORM,手动执行SQL查询(这会使您的代码特定于数据库)。

  3. 在数据库中为增量创建另一个字段,并在保存时更新它(如果您想要特定于数据库,则使用触发器进行更新)。

答案 4 :(得分:1)

对于你想要的东西,只需在模型中添加一个新方法,然后在DB上进行聚合,然后计算两个总和之间的差值。像这样:

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'))