Examples

Dependency Examples

The following examples may give you a first idea on how to use the depends keyword of the computed decorator for basic dependency types.

No dependencies

The most basic example is a computed field, that has no field dependencies at all. It can be constructed by setting depends to an empty container, e.g.:

class MyComputedModel(ComputedFieldsModel):

    @computed(Field(...), depends=[])
    def comp(self):
        return some_value_pulled_from_elsewhere

Such a field will only be recalculated by calling save() or save(update_fields=[comp, ...]) on a model instance. It will not be touched by the auto resolver, unless you force the recalculating by directly calling update_dependent(MyComputedModel.objects.all(). update_fields=None).

Note

The empty container is currently needed due to the transition from the old depends syntax to the new one. Until support for the old syntax gets removed, there is a shim in place, that automatically expands depends=None to depends=[['self', list_of_local_concrete_fields]].

Dependency to local fields

A more useful computed field example would do some calculation based on some other model local fields:

class MyComputedModel(ComputedFieldsModel):
    fieldA = Field(...)
    fieldB = Field(...)

    @computed(Field(...), depends=[['self', ['fieldA', 'fieldB']]])
    def comp(self):
        return some_calc(self.fieldA, self.fieldB)

This can be achieve in a safe manner by placing a self rule in depends as shown above.

Background on self rule

At a first glance it seems weird, that you should declare dependencies on model local fields. Well in previous versions it was not needed at all, but turned out as a major shortcoming of the old depends syntax leading to unresolvable ambiguity. The new syntax and the need to put local fields in a self rule enables django-computedfields to properly derive the execution order of local computed fields (MRO) and to correctly expand on update_fields given to a partial save call.

Warning

Technically the self rule is not needed, if there are no other local computed fields depending on comp and you always do a full instance save. Although this might be the case for 80% of the trivial use cases, it will certainly break under more advanced scenarios, like third party apps doing partial updates with save(update_fields=[...]) or using bulk actions. Thus it is a good idea, to apply a self rule right from the beginning listing all local concrete field sources.

Dependency to computed fields

To depend on another local computed field, simply list it in the self rule:

class MyComputedModel(ComputedFieldsModel):
    fieldA = Field(...)
    fieldB = Field(...)
    fieldC = Field(...)

    @computed(Field(...), depends=[['self', ['fieldA', 'fieldB']]])
    def comp(self):
        return some_calc(self.fieldA, self.fieldB)

    @computed(Field(...), depends=[['self', ['fieldC', 'comp']]])
    def final(self):
        return some__other_calc(self.fieldC, self.comp)

The auto resolver will take care, that the computed fields are calculated in the correct order (MRO). In the example above it will make sure, that final gets recalculated after comp. This also works with a partial save with save(update_fields=['fieldA']), given that fieldA was changed. For that the resolver expands update_fields internally to ['fieldA', 'comp', 'final'].

Note

For correct MRO resolving computed fields should never be omitted in the self dependency rule, otherwise the result of dependent computed fields is undetermined.

The ability to depend on other computed fields introduces the problem of possible update cycles:

class MyComputedModel(ComputedFieldsModel):
    fieldA = Field(...)
    fieldB = Field(...)
    fieldC = Field(...)

    @computed(Field(...), depends=[['self', ['fieldA', 'fieldB', 'final']]])
    def comp(self):
        return some_calc(self.fieldA, self.fieldB)

    @computed(Field(...), depends=[['self', ['fieldC', 'comp']]])
    def final(self):
        return some__other_calc(self.fieldC, self.comp)

There is no way to create or update such an instance, as comp relies on final, which itself relies on comp. Here the the dependency resolver will throw a cycling exception during startup. Note that self dependencies always must be cycle-free.

Optimization Examples

Todo

To be written:

  • select_related example
  • prefetch_related example
  • notes on complicated dependencies
  • Possible savings on using update_fields
  • some more guidance for bulk actions and update_dependent
  • TBD