Skip to content

Formula

💡 TL;DR - Derive attribute using other attributes of current/parent class

Formula rules are lambda expressions or functions that can reference other attributes of current/parent class. They are declared in the declare_logic() function in logic/declare_logic.py.

Execution order is system-determined based on dependencies.

Changes to referenced parent attributes are propagated to all child rows. Contrast this to the Copy rule, for parent references where you do not want propagation.

Formulas may be pruned if there are no changes to the referenced attributes.

 

Provide expression as lamda

Simple formulas are most easily expressed as lambda functions:

Rule.formula(derive=models.OrderDetail.Amount,  # compute price * qty
        as_expression=lambda row: row.UnitPrice * row.Quantity)

 

Provide expression as function

   def derive_amount(row: models.Item, old_row: models.Item, logic_row: LogicRow):
       amount = row.Quantity * row.UnitPrice
       if row.Product.CarbonNeutral and row.Quantity >= 10:
           amount = amount * Decimal(0.9)
       return amount


   Rule.formula(derive=models.Item.Amount, calling=derive_amount)

 

How parent references are detected

Pruning and parent-change propagation both depend on the system recognizing which parent attributes a formula references - it does this by scanning the rule's own source text for row.<parent>.<attribute>, e.g. row.Product.CarbonNeutral above.

If a calling= function's parent access happens inside a helper function it calls (rather than appearing directly in the function you pass to calling=), the scan won't see it - the formula will be wrongly pruned on updates, and won't recompute when the parent attribute changes. Fix this by adding a # deps: comment naming the references directly in the function body - the scan reads comments too:

def _quantity(row, old_row, logic_row):
    # deps: row.OrderLine.Quantity row.OrderLine.DateServed
    return _derive_quantity(row, logic_row)  # parent access happens inside here

Rule.formula(derive=models.Movement.Quantity, calling=_quantity)