一个信号m2m_changed和post_remove的bug

时间:2012-07-27 10:40:14

标签: django django-signals

我需要检测一个post_remove信号,所以我写了:

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)

如果我用'post_add'改变'post_remove'就可以了..这是关于post_remove的django的错误吗?

我使用该模型,并在“订阅”的两个值之间切换(所以添加了一个,删除了一个)

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'})

我看过一篇有django错误的帖子,也许它还没有被修复......(或者它是我^^)

3 个答案:

答案 0 :(得分:6)

据我所知,这不是一个错误,只是Django没有以你期望的方式更新m2m关系。它不会删除要删除的关系,然后添加新的关系。相反,它清除所有m2m关系,然后再添加它们。

有一个相关问题Django signal m2m_changed not triggered,它链接到故障单13087

因此,您可以使用pre_clear信号检查post_clearm2m_changed操作,但由于这些操作未提供pk_set,因此无法找到保存前的相关条目,如您在your other question中所做的那样。

答案 1 :(得分:6)

感谢Alasdairs comment我找到了解决方案并将其发布在此处 - 也许有人可以使用它。

<强> 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)

答案 2 :(得分:1)

经过很长一段时间的深入了解,我得出了一个结论 首先我的问题:我有一些如何更新我的模型中的一个属性,当我的m2m为空时将其设置为False,如果它至少有一个项目则为true,那么,真的有效但是当我尝试“pre_remove”或者“post_remove”永远不会被触发,所以在一些不同的例子之后,我在这个“pre_clear”上看到了一些奇怪的东西,每当我改变我的m2m时,它总是有最后的值,所以我设法强制从我的m2m中删除这个值这样它会触发pre_remove和post_remove,所以这对我有用,请看下面的代码

所以现在我可以根据我的m2m

自动设置ativo True或False
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 )