【问题标题】:Return function as a field on django model返回函数作为 django 模型上的字段
【发布时间】:2011-01-13 13:45:25
【问题描述】:

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

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

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

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

如何做到这一点?

【问题讨论】:

  • 所有答案都是正确的,它们都会导致两个浮点字段的差异,但我最终使用了@daniel 解决方案,它工作正常。

标签: django django-models django-templates


【解决方案1】:

一种方法是创建一个管理器并使用原始 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的复制粘贴 然后我们可以使用通用视图的额外上下文字典中的管理器功能。并进行代数运算而不会不必要地访问数据库。

更好的解决方案是使用自定义管理器创建您自己的 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】:

    你试过了吗?

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

    【讨论】:

      【解决方案3】:

      对于您想要的,只需向模型添加一个新方法并在数据库上进行聚合,然后只需计算两个总和之间的差即可。像这样:

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

      【讨论】:

        【解决方案4】:

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

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

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

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

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

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

        【讨论】:

          【解决方案5】:

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

          您还必须在 Python 中进行求和:

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

          【讨论】:

          • 不是 lambda。 sum 函数在 list comprehension 上运行
          • 这个问题如何回答或帮助解决问题?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-05-10
          • 2014-01-03
          • 2011-01-19
          • 1970-01-01
          • 1970-01-01
          • 2018-04-06
          • 2012-03-02
          相关资源
          最近更新 更多