django - 在模型级别的字段中存储计算值时获得的奇怪结果(缓存?)

时间:2011-08-30 17:54:31

标签: django django-models

亲爱的django大师,

请抓住我的鼻子,把它贴在我愚蠢的错误发光的地方。

我准备基于现有字段值进行一些简单的数学运算,并将其存储在当前实例的单独字段(小计)中。

我的问题实际上已包含在最后一段代码中。

class Element(models.Model):
    name = models.CharField(max_length=128)
    kids = models.ManyToManyField('self', null=True, blank=True, symmetrical=False)
    price = models.IntegerField('unit price', null=True, blank=True)
    amount = models.IntegerField('amount', null=True, blank=True)
    subtotal = models.IntegerField(null=True, blank=True)
    def counter(self):
        output = 0
        # Check if this is the lowest hierarchy level (where the intention is to 
        # store both values for price and amount) and proceed calculations 
        # based on input values. Also check if values are set to avoid operations
        # with incompatible types.
        # Else aggregate Sum() on subtotal values of all kids.
        if self.kids.count() == 0:
            if self.price == None or self.amount == None:
                output = 0
            else:
                output = self.price * self.amount
        else:
            output = self.kids.aggregate(Sum('subtotal'))['subtotal__sum']
        self.subtotal = output
    def __unicode__(self):
        return self.name

这就是我的样本数据的样子(如果我错过了一些如何展示它的惯例,我很抱歉。)

element.name = son
element.kids = # None
element.price = 100
element.amount = 1
element.subtotal = 100 # Calculates and stores correct value.

element.name = daughter
element.kids = # None
element.price = 200
element.amount = 5
element.subtotal = 1000 # Calculates and stores correct value.

element.name = father
element.kids = son, daughter
element.price = # None. Use this field for overriding the calculated
                # price at this level.
element.amount = # None.
element.subtotal = 25   # Calculates completely irrelevant value. 
                        # The result seems to be some previous state 
                        # (first maybe) of subtotal of one of the kids.
                        # This is where my cache part of question comes from.

在解决这个简单的任务时,我已经开始使用clean()类,但结果是在第二次保存之后计算的(可能是另一个问题的好主题)。我切换到自定义方法。但是现在经过一个晚上的花费,我会很自豪地使用Owl先生对小熊维尼的话:“你先生卡住了!”此时我只使用原生的django管理表单。任何帮助都将非常感激。

1 个答案:

答案 0 :(得分:0)

如果没有看到您用来调用所有这些计算的代码,我只能猜出问题可能是什么。显示您的视图代码(或其他)可能会有所帮助,它会“开始”小计的计算。

我可以在这里看到两个潜在的问题。

第一种是你的反方法。您是在计算总数后保存模型实例吗?

def counter(self):
    output = 0
    if self.kids.count() == 0:
        if self.price == None or self.amount == None:
            output = 0
        else:
            output = self.price * self.amount
    else:
        output = self.kids.aggregate(Sum('subtotal'))['subtotal__sum']
    self.subtotal = output
    self.save() # save the calculation

如果不保存实例,当您从父级查询子级时,将获得当前数据库值而不是新计算的值。这可能是也可能不是问题,具体取决于开始调用counter的代码。

我在这里看到的另一个潜在问题是缓存失效。如果孩子更新了他们的priceamount,是否已通知家长,以便其也可以更新其subtotal?您可以在最后一刻覆盖save方法进行计算。

def save(self, *args, **kwargs):
    self.counter() # calculate subtotal
    super(Element, self).save(*args, **kwargs) # Call the "real" save() method.
    for parent in self.element_set.all(): # use a related name instead
        parent.save() # force recalculation of each parent

您会注意到这只会在仅保存后强制正确的小计值有效。请注意,以这种方式覆盖save方法与我在计算计数器时保存实例的第一个解决方案完全矛盾。如果你同时使用两者,你​​将获得一个StackOverflow,因为计算器然后被保存,然后在计算保存计数器期间,这将触发另一次保存。坏消息。