Home > OS >  How to know when chained django lifecycle hooks/signals are finished running?
How to know when chained django lifecycle hooks/signals are finished running?

Time:09-22

I have an Animal model and a Herd model that looks like this:

from django_lifecycle.hooks import AFTER_CREATE

class Animal(models.Model):
    parent = OneToOneField("self", related_name="child")
    age = IntegerField()

class Herd(models.Model):
    animal = OneToOneField(Animal)
    total_age = IntegerField()
    
    @hook(AFTER_CREATE)
    def create_animal_child(self):
        if self.animal.child.exists():
            Herd.objects.create(parent=self, animal=self.animal.child)

    def save_total_age(self):
        total_age = self.animal.age
        position = self.animal
        while position.child.exists():
            total_age  = position.child.age
            position = position.child

The idea being that you create your Animals. Then you create your Herd for the topmost animal you want. The Herd's AFTER_CREATEwill then create a "chain" of all the Herd objects for the Animal's children, the children's children, and so on.

E.g.

a1 = Animal.objects.create(age=10)
a2 = Animal.objects.create(parent=a1, age=7)
a3 = Animal.objects.create(parent=a2, age=3)

h1 = Herd.objects.create(animal=a1)
# Two Herd objects with an "animal" field pointing to a2 and a3 respectively are automatically created

h1.save_total_age() # h1.total_age = 20 # 10   7   3

Which is all fine.

My problem is for my front-end, how can I tell when all the relevant Animal AFTER_CREATE hooks have finished running so I know to call my calculate_total_age method?

So say I have the following REST API endpoints:

example.com/api/animals/<pk>
example.com/api/herds/<pk>
example.com/api/herds/<pk>/save_total_age

If I do a POST to example.com/api/herds/<pk> to create a Herd, I call save_total_age() as soon as that "parent" Herd is created - and not necessarily after all the "children" Herds have been created via the AFTER_CREATE hook.

axios.post(
    "example.com/api/herds/",
    {animal_id: 1}
).then((response) => {
    axios.post(
        `example.com/api/herds/${response.data.pk}/save_total_age`,
        {},
    )
});

How can I set my backend signals and/or frontend async/await so that save_total_age() only runs after my AFTER_CREATE chain is complete?

Edit: My best solution right now is to add some condition to save_word_count() that makes sure the Herd chain has the same # of objects as the Animal chain. If so, run the method. If not, return a bad request and retry the POST.

CodePudding user response:

Signals (which are called by those hooks) run synchronously, not asynchronously. So when you call create() for your first heard, it will always run the AFTER_CREATE hook and create the 2nd Heard object, before it returns and let you run the save_total_age() method.

In other words this should work just fine as it is.

  • Related