rsinger86/django-lifecycle

Many to many fields

danialb007 opened this issue · 2 comments

Greetings.
I've been working on a project and i wanted to fire a hook after an object is created and immediately use its m2m field's value and process some stuff.
AFTER_CREATE and AFTER_SAVE hooks are not working as expected in this case. (even with on_commit=True)
so if field (X) is a ManyToManyField you would write code like this:

@hook(AFTER_CREATE)
    def my_hook(self):
        print(self.X.all())

and this results in an empty queryset.

Django has a built-in signal called m2m_changed and it works as i expected but the syntax is messy and thats the main reason i preferred this package.

tl;dr

AFAIK this is not possible with django-lifecycle, for this use case stick to the signal or create a function that handle the whole behaviour (creating object, adding m2m and doing the "after create" thing.

Explanation

A m2m relation what it does is creating another table that contains a foreing key to both tables:

class Person(models.Model):
    ...

class Group(models.Model):
    members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)

(If you don't specify this through model explicitly, it's created automatically)
So... in order to add a Membership you need before to have a Person and a Group saved (because you need their ids)

So, if we add a hook to either side of the M2M, it will be called before the through instance are created:

class Person(LifecycleModel):
    
    @hook(AFTER_CREATE)
    def my_hook(self):
        print(self.X.all())

class Group(models.Model):
    members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)

When you add or remove items for a M2M relationship, the save method is not called, but signals do.

Thanks.
Actually makes sense and i kind of knew that, just thought it's somehow possible and i didn't know.
Closing this issue.