In Django, how do I get a signal for when a Group

2019-07-04 04:54发布

In the Django admin I sometimes add or delete users to or from (existing) groups. When this happens I'd like to be able to run a function.

I'm just using the standard User and Group models.

I have looked at doing it with signals, through m2m_changed, but it seems to need a Through class - and I don't think there is one in this case.

4条回答
我想做一个坏孩纸
2楼-- · 2019-07-04 05:25

From the django doc:

sender - The intermediate model class describing the ManyToManyField. This class is automatically created when a many-to-many field is defined; you can access it using the through attribute on the many-to-many field.

When subscribing to m2m_changed like so:

@receiver(m2m_changed)
def my_receiver(**kwargs):
    from pprint import pprint
    pprint(kwargs)

You will receive a bunch of signals like this (shortened):

{'sender': <class 'django.contrib.auth.models.User_groups'>,
 'action': 'post_add',
 'instance': <User: bouke>,
 'model': <class 'django.contrib.auth.models.Group'>,
 'pk_set': set([1]),
 'reverse': False,
 'signal': <django.dispatch.dispatcher.Signal object at 0x101840210>,
 'using': 'default'}

So the user bouke has been added to pk_set groups: [1]. However I noted that the admin layout clears all groups and then adds the selected groups back in. The signals you will receive are pre_clear, post_clear, pre_add, post_add. Using a combination of these signals you could store the pre and post groups. Doing a diff over these lists, you have the deleted and added groups for the user.

Note that the signals are the other way around (pk_set and instance) when editing a group instead of a user.

查看更多
男人必须洒脱
3楼-- · 2019-07-04 05:29

You'll see in the Django documentation (v1.11) that your desired sender should be the intermediate through field belonging to the ManyToMany field, wherever that's defined. If you register that as your sender, then you'll be listening to eg Users adding Groups to themselves, as well as Groups adding Users to themselves.

self.walrus.groups.remove(self.peon_group)

@receiver(signal=m2m_changed, sender=User.groups.through)
def adjust_group_notifications(instance, action, reverse, model, pk_set, using, *args, **kwargs):
    if model == Group and not reverse:
        logger.info("User %s is modifying their relation to groups «%s»", instance.username, pk_set)
        …
        # Walrus example fits here
    else:
        logger.info("Group %s is modifying its relation to users «%s»", instance, pk_set)
        …
    return
查看更多
萌系小妹纸
4楼-- · 2019-07-04 05:30

You need to create a signal using m2m_changed as a receiver. According to the official Django documentation:

A signal is sent when a ManyToManyField is changed on a model instance. Strictly speaking, this is not a model signal since it is sent by the ManyToManyField.

So, the simplest implementation is as follows:

@receiver(m2m_changed)
def signal_handler(**kwargs):
    from pprint import pprint
    pprint(kwargs)

In your case, you want to perform something when a user is added or removed from a group, so you can take advantage of the action parameter when it takes the values 'pre_add', 'post_add', 'pre_remove', and 'post_remove'. You can also take advantage of pk_set parameter which contains primary key values that have been added to or removed from the relation.

@receiver(m2m_changed)
def signal_handler_when_user_is_added_or_removed_from_group(action, instance, pk_set, model, **kwargs):
    if model == Group:
        if action == 'pre_add':
            # TODO: add logic
            pass
        elif action == 'post_add':
            # TODO: add logic
            pass
        # handle as many actions as one needs
    # The following for loop prints every group that were
    # added/removed.
    for pk in pk_set:
        group = Group.objects.get(id=pk)
        print(group)
查看更多
仙女界的扛把子
5楼-- · 2019-07-04 05:31

It might be better to try and achieve this with django-celery, that way you can write custom tasks, and based on a certain criteria (such as removal or addition) you can fire of a certain task.

查看更多
登录 后发表回答