On Thursday 16 November 2017 14:08:20 Sjoerd Job Postmus wrote:
> I disagree with not being able to calculate it locally (without the
> database): such a calculation depends on other fields. What if a dependent
> field is updated, but the object is not yet saved. What should the value
> be?
> 
> >>> c.age, c.is_adult
> 
> 17, False
> 
> >>> c.age = 40
> >>> c.age, c.is_adult
> 
> 40, False
> 
> Would not make sense to me.
> 

While I agree with the gist of this argument, I don't believe in parallel 
computations -- that is, having some database-definition of a computed column 
emulated in Python. Such computations always end up slightly different in some 
edge cases (different precision, different rounding rules, different timezones 
between webserver and database server, etc) and this can lead to obscure 
heizenbugs and weird, hard-to-track data corruptions.

I see three solutions:

1) Make all computations in Python. This means the feature can be supported 
only on databases which can run Python as part of their queries; I'm not quite 
sure about the details, but I think PG can. I'm certain none of the other core 
databases can, perhaps MSSQL can with IronPython.

2) Just invalidate all calculated columns as soon a change in the model 
occurs. So then,

        >>> c.age, c.is_adult

        17, False

        >>> c.age = 40
        >>> c.is_adult

        StaleCalculatedFieldException

3) If we claim that the calculated field is, essentially, a declarative 
shorthand for .annotate(), then treat it as such -- and live with the result 
whcih Sjoerd Job described as nonsensical. However, to make it a little less 
nonsensical, we'd call the field something like "DBCalculatedField", to point 
out that it isn't updated automatically in the model instance.

BTW, with annotate we already have that "nonsensical" behavior today, and 
people seem to accept it:

        class Teacher(models.Model):
                pass

        class Student(models.Model):
                teacher = models.ForeignKey(Teacher)

        ...

        >>> t = Teacher.objects.annotate(std_count=Count('student')).get(pk=1)
        >>> t.std_count
        16
        >>> Student.objects.create(teacher=t)
        >>> t.std_count
        16

I understand that the perception may be different for calculated fields which 
depend purely on "in-record" fields -- but I don't see a good way to verify 
that property on the caclulated field definition, so I suspect the best (or, 
rather, least bad) solution is just to change that perception.

Shai

Reply via email to