Search code examples
djangodjango-modelsdjango-admindjango-signals

Django change the order of pre_save post_save signals being called when using inlines?


I have an Order (1) and OrderLine (n) model, here order can have multiple order-lines. This is all run from inside the Django-admin, where the OrderLine is setup as part of the inlines on OrderAdmin(admin.ModelAdmin).

Simplified like this:

   class OrderLine(admin.StackedInline):
     pass

   @admin.register(Order)
   class OrderAdmin(admin.ModelAdmin):
     inlines = [OrderLine]

I registered for the pre_save and post_save on on both models. Django calls these signals in the following order:

  1. Order | pre_save
  2. Order | post_save
  3. OrderLine 1 | pre_save
  4. OrderLine 1 | post_save
  5. OrderLine 2 | pre_save
  6. OrderLine 2 | post_save
  7. OrderLine n | pre_save
  8. OrderLine n | post_save

The issue I'm having is that I would like to change the order of the signals called, as follows:

  1. Order | pre_save
  2. OrderLine 1 | pre_save
  3. OrderLine 1 | post_save
  4. OrderLine 2 | pre_save
  5. OrderLine 2 | post_save
  6. OrderLine n | pre_save
  7. OrderLine n | post_save
  8. Order | post_save

Since I need to do a few calculations in each OrderLine, and those results needs to be used in the Order post. But the post signal is already been called.

The only solution I see is to call my code on each OrderLine post signal, which is a bit redundant, especially when you have many order-lines.

What would be the best / better way to tackle this?


Solution

  • I have been side-tracked, thinking you should solve this via signals.

    Apparently you can (and should) implement in your admin class (admin.ModelAdmin) the following method:

     # example
     def save_related(self, request, form, formsets, change):
        super(OrderAdmin, self).save_related(request, form, formsets, change)
        # get the master obj
        order = form.instance
        order.total = Decimal.from_float(0.0)
        for ol in order.order_lines.all():
            order.total += ol.total order.total
    

    This is called after all related objects are updated.