Search code examples
djangotriggerswagtaildjango-signalswagtail-admin

Django: How to trigger a function whenever certain objects are modified from the wagtail interface


I have a long list of "Quotation" objects.

The price of a quotation depends on dozens of children (and grand children) objects. The lowest one being the rate/hour.

When I change the children objects of a quotation like the rate/hour, the quotation price changes.

I would like to recalculate the price of each quotation that is impacted by any change I make on their consistuant objects automatically. I am using Wagtail for the object admin.

I am not sure about the way to do that, should I use signals? Wagtail hooks?


Solution

  • Wagtail models and pages are just Django models.

    Are all children (and grand children) of the same model? In that case you could just register a post_save signal on the child model, but in general, I'd recommend using post_save only for non-mutating actions like sending an email when an object changes, unless you take the following things into consideration:

    • This kind of processing can become very slow very quickly. If you have multiple quotations, you'll need to use an atomic transaction, otherwise you'll make a new db connection when saving every quotation.
    • You'll also need to prefetch children and grandchildren to prevent multiple database calls.
    • When saving other models inside a post_save you run the risk of creating an endless loop of post_save signals.

    I think a better way would be to add a @property called price on Quotation instead of storing the price in the database:

    class Quotation(models.Model):
        # Model fields here
        ...
    
        @property
        def price(self):
            return sum([child.price for child in self.children])
    

    This will only calculate a price when you call quotation.price. You could speed up this calculation by prefetching children when receiving Quotations.