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