M2m_changed signal and error with post_remove

I need to detect the post_remove signal, so I wrote:

def handler1(sender, instance, action, reverse, model, pk_set, **kwargs): if (action == 'post_remove'): test1() # not declared but make a bug if it works, to detect :) m2m_changed.connect(handler1, sender=Course.subscribed.through) 

If I changed 'post_remove' to 'post_add', that's fine. Is this django error about post_remove ??

I use this model and I switch between two “signed” values ​​(so that one is added and one is deleted)

 class Course(models.Model): name = models.CharField(max_length=30) subscribed = models.ManyToManyField(User, related_name='course_list', blank=True, null=True, limit_choices_to={'userprofile__status': 'student'}) 

I saw a django error message, maybe it has not been fixed ... (or is it me ^^)

+6
source share
3 answers

As I understand it, this is not a mistake, just Django does not update the m2m relationship as you expect. It does not delete the relationships that need to be deleted, and then add new ones. Instead, it clears all m2m relationships and then adds them again.

There is a related question Django signal m2m_changed does not start , which refers to ticket 13087 .

So, you can check the pre_clear or post_clear with the m2m_changed signal, but since these actions do not provide pk_set , this will not help you find the appropriate entries before saving, as you wanted to do in your other question .

+5
source

Thanks to Alasdairs comment, I found a solution and will post it here - maybe someone can use it.

models.py

 class Team(models.Model): name = models.CharField(max_length=100) members = models.ManyToManyField(User) pre_save.connect(team_pre_save, sender=Team) m2m_changed.connect(team_members_changed, sender=Team.members.through) 

signals.py

 def team_pre_save(sender, instance, **kwargs): if instance.pk: instance._old_m2m = set(list(instance.members.values_list('pk', flat=True))) else: instance._old_m2m = set(list()) def team_members_changed(sender, instance, **kwargs): if kwargs['action'] == "post_clear": # remove all users from group group = Group.objects.get(name='some group') for member in instance._old_m2m: user = User.objects.get(pk=member) user.groups.remove(group) if kwargs['action'] == "post_add": added_members = list(kwargs['pk_set'].difference(instance._old_m2m)) deleted_members = list(instance._old_m2m.difference(kwargs['pk_set'])) if added_members or deleted_members: # we got a change - do something, for example add them to a group? group = Group.objects.get(name='some group') for member in added_members: user = User.objects.get(pk=member) user.groups.add(group) for member in deleted_members: user = User.objects.get(pk=member) user.groups.remove(group) 
+6
source

I came to a conclusion after a long, long time My problem first: I had some update of one attribute from my model to set it to False when my m2m is empty, and true if it has at least 1 element, so the true thing works, but when I try "pre_remove" or "post_remove" never starts, so after some attempts with some examples of differences, I saw something strange in this "pre_clear", every time I change my m2m, it always has the last values, so I manage to forcibly remove these values ​​from mine of m2m and so he starts pre_remove and post_remove, so it works for me. See below code

So now I can automatically set ativo True or False based on my m2m

 class Servico(BaseMixin): descricao = models.CharField(max_length=50) #This inheritance from User of django that has is_active boolean field class UsuarioRM(Usuario): servicos = models.ManyToManyField(Servico,related_name='servicos_usuario', blank=True) # SIGNALS from django.db.models import signals from django.db.models.signals import m2m_changed def usuariorm_servicos_changed(sender, **kwargs): action = kwargs.pop('action', None) pk_set = kwargs.pop('pk_set', None) instance = kwargs.pop('instance', None) if action == "pre_clear": if instance.servicos.all(): servicos = instance.servicos.all() for servico in servicos: instance.servicos.remove(servico) instance.save() else: instance.is_active = False instance.save() if action == "post_add": if pk_set: instance.is_active = True else: instance.is_active = False instance.save() m2m_changed.connect( usuariorm_servicos_changed, sender=UsuarioRM.servicos.through ) 
+1
source

Source: https://habr.com/ru/post/921473/


All Articles